• 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 <random>
25 
26 #if defined(CONFIG_HAS_SYSPARA)
27 #include <parameters.h>
28 #endif
29 #include <sys/stat.h>
30 #include <sys/utsname.h>
31 #include <unistd.h>
32 
33 #include "command.h"
34 #include "debug_logger.h"
35 #include "hiperf_client.h"
36 #include "ipc_utilities.h"
37 #if defined(is_ohos) && is_ohos
38 #include "hiperf_hilog.h"
39 #endif
40 #include "option.h"
41 #include "perf_event_record.h"
42 #include "perf_file_reader.h"
43 #include "utilities.h"
44 #include "subcommand_report.h"
45 
46 using namespace std::chrono;
47 namespace OHOS {
48 namespace Developtools {
49 namespace HiPerf {
50 const std::string PERF_CPU_TIME_MAX_PERCENT = "/proc/sys/kernel/perf_cpu_time_max_percent";
51 const std::string PERF_EVENT_MAX_SAMPLE_RATE = "/proc/sys/kernel/perf_event_max_sample_rate";
52 const std::string PERF_EVENT_MLOCK_KB = "/proc/sys/kernel/perf_event_mlock_kb";
53 const std::string SCHED_SWITCH = "/sys/kernel/tracing/events/sched/sched_switch/enable";
54 const std::string SCHED_SWITCH_DEBUG = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable";
55 const std::string PROC_VERSION = "/proc/version";
56 const std::string SAVED_CMDLINES_SIZE = "/sys/kernel/tracing/saved_cmdlines_size";
57 
58 // when there are many events, start record will take more time.
59 const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms;
60 const int CONTROL_WAIT_RESPONSE_TIMEOUT = 35000;  // 35s: Timeout for main process wait app restart finish
61 constexpr const uint8_t WAIT_TIMEOUT = 40;  // 40s: Timeout for the main process exit
62 
63 constexpr uint64_t MASK_ALIGNED_8 = 7;
64 constexpr size_t MAX_DWARF_CALL_CHAIN = 2;
65 constexpr uint64_t TYPE_PERF_SAMPLE_BRANCH = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
66                                              PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_JUMP |
67                                              PERF_SAMPLE_BRANCH_IND_CALL | PERF_SAMPLE_BRANCH_COND |
68                                              PERF_SAMPLE_BRANCH_CALL;
69 static constexpr uint64_t PIPE_READ = 0;
70 static constexpr uint64_t PIPE_WRITE = 1;
71 static constexpr uint64_t CHECK_WAIT_TIME_MS = 200;
72 static constexpr uint32_t MAX_SERVER_OUTPUT_WAIT_COUNT = 600;
73 static std::atomic_bool g_callStop(false);
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     }
109     return it->second;
110 }
111 
~SubCommandRecord()112 SubCommandRecord::~SubCommandRecord()
113 {
114     CloseReplyThread();
115     CloseClientThread();
116     if (readFd_ != -1) {
117         close(readFd_);
118         readFd_ = -1;
119     }
120     if (writeFd_ != -1) {
121         close(writeFd_);
122         writeFd_ = -1;
123     }
124 }
125 
DumpOptions() const126 void SubCommandRecord::DumpOptions() const
127 {
128     printf("DumpOptions:\n");
129     printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
130     printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
131     printf(" timeStopSec:\t%f sec\n", timeStopSec_);
132     printf(" frequency:\t%d\n", frequency_);
133     printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
134     int i = 0;
135     for (auto &group : selectGroups_) {
136         i++;
137         printf(" selectGroups:\t%2d:%s\n", i, VectorToString(group).c_str());
138     }
139     printf(" no_inherit:\t%s\n", noInherit_ ? "true" : "false");
140     printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
141     printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
142     printf(" excludeTids:\t%s\n", VectorToString(excludeTidArgs_).c_str());
143     printf(" excludeThreads:\t%s\n", VectorToString(excludeThreadNameArgs_).c_str());
144     printf(" excludeProcessName_:\t%s\n", VectorToString(excludeProcessNameArgs_).c_str());
145     printf(" kernelCallChain:\t%s\n", kernelCallChain_ ? "true" : "false");
146     printf(" callChainUserOnly_:\t%s\n", callChainUserOnly_ ? "true" : "false");
147     printf(" restart:\t%s\n", restart_ ? "true" : "false");
148     printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
149     printf(" excludePerf:\t%d\n", excludeHiperf_);
150     printf(" cpuPercent:\t%d\n", cpuPercent_);
151     printf(" offCPU_:\t%d\n", offCPU_);
152     printf(" delayUnwind_:\t%d\n", delayUnwind_);
153     printf(" disableUnwind_:\t%d\n", disableUnwind_);
154     printf(" disableCallstackExpend_:\t%d\n", disableCallstackExpend_);
155     printf(" enableDebugInfoSymbolic:\t%d\n", enableDebugInfoSymbolic_);
156     printf(" symbolDir_:\t%s\n", VectorToString(symbolDir_).c_str());
157     printf(" outputFilename_:\t%s\n", outputFilename_.c_str());
158     printf(" appPackage_:\t%s\n", appPackage_.c_str());
159     printf(" checkAppMs_:\t%d\n", checkAppMs_);
160     printf(" clockId_:\t%s\n", clockId_.c_str());
161     printf(" mmapPages_:\t%d\n", mmapPages_);
162     printf(" dataLimit:\t%s\n", strLimit_.c_str());
163     printf(" callStack:\t%s\n", VectorToString(callStackType_).c_str());
164     printf(" branchSampleTypes:\t%s\n", VectorToString(vecBranchFilters_).c_str());
165     printf(" trackedCommand:\t%s\n", VectorToString(trackedCommand_).c_str());
166     printf(" pipe_input:\t%d\n", clientPipeInput_);
167     printf(" pipe_output:\t%d\n", clientPipeOutput_);
168     printf(" cmdlinesSize_:\t%d\n", cmdlinesSize_);
169     printf(" report_:\t%s\n", report_ ? "true" : "false");
170     printf(" backtrack_:\t%s\n", backtrack_ ? "true" : "false");
171     printf(" backtrackTime_:\t%" PRIu64 "\n", backtrackTime_);
172 }
173 
GetSpeOptions()174 bool SubCommandRecord::GetSpeOptions()
175 {
176     std::string speName;
177 
178     for (size_t i = 0; i < selectEvents_.size(); i++) {
179         std::string optionValue = selectEvents_[i];
180 
181         std::vector<std::string> valueExpressions = StringSplit(optionValue, "/");
182         if (i == 0) {
183             if (valueExpressions.size() > 1) {
184                 speName = valueExpressions[0];
185                 valueExpressions.erase(valueExpressions.begin());
186             } else {
187                 break;
188             }
189         }
190 
191         for (auto item: valueExpressions) {
192             std::vector<std::string> expressions = StringSplit(item, "=");
193             size_t itemNum = 2;
194             if (expressions.size() == itemNum && (IsNumeric(expressions[1]) || IsHexDigits(expressions[1]))) {
195                 std::string name = expressions[0];
196                 uint64_t num = 0;
197                 char *endPtr = nullptr;
198                 errno = 0;
199                 if (IsNumeric(expressions[1])) {
200                     num = std::strtoull(expressions[1].c_str(), &endPtr, 10); // 10 : decimal scale
201                 } else {
202                     num = std::strtoull(expressions[1].c_str(), &endPtr, NUMBER_FORMAT_HEX_BASE);
203                 }
204                 if (endPtr == expressions[1].c_str() || *endPtr != '\0' || errno != 0) {
205                     HLOGE("string to uint64_t failed, expressions[1]: %s", expressions[1].c_str());
206                     continue;
207                 }
208                 if (speOptMap_.find(name) != speOptMap_.end()) {
209                     speOptMap_[name] = num;
210                 }
211             }
212         }
213     }
214     if (speName.size() > 0) {
215         selectEvents_.clear();
216         selectEvents_.emplace_back(speName);
217     }
218     return true;
219 }
220 
GetOptions(std::vector<std::string> & args)221 bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
222 {
223     if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
224         return false;
225     }
226     if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
227         HLOGD("-a option needs root privilege for system wide profiling.");
228         printf("-a option needs root privilege for system wide profiling.\n");
229         return false;
230     }
231     if (!Option::GetOptionValue(args, "--exclude-hiperf", excludeHiperf_)) {
232         return false;
233     }
234     if (!Option::GetOptionValue(args, "--control", controlCmd_)) {
235         return false;
236     }
237     allowIpc_ = controlCmd_ != CONTROL_CMD_PREPARE;
238     if (!Option::GetOptionValue(args, "-z", compressData_)) {
239         return false;
240     }
241     if (!Option::GetOptionValue(args, "--no-inherit", noInherit_)) {
242         return false;
243     }
244     if (!Option::GetOptionValue(args, "--offcpu", offCPU_)) {
245         return false;
246     }
247     if (!Option::GetOptionValue(args, "--delay-unwind", delayUnwind_)) {
248         return false;
249     }
250     if (!Option::GetOptionValue(args, "--disable-unwind", disableUnwind_)) {
251         return false;
252     }
253     if (!Option::GetOptionValue(args, "--disable-callstack-expand", disableCallstackExpend_)) {
254         return false;
255     }
256     if (!Option::GetOptionValue(args, "--enable-debuginfo-symbolic", enableDebugInfoSymbolic_)) {
257         return false;
258     }
259     if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
260         return false;
261     }
262     if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
263         return false;
264     }
265     if (!GetOptionFrequencyAndPeriod(args)) {
266         return false;
267     }
268     if (!Option::GetOptionValue(args, "--cpu-limit", cpuPercent_)) {
269         return false;
270     }
271     if (!Option::GetOptionValue(args, "-m", mmapPages_)) {
272         return false;
273     }
274     if (!Option::GetOptionValue(args, "--symbol-dir", symbolDir_)) {
275         return false;
276     }
277     if (!Option::GetOptionValue(args, "-o", outputFilename_)) {
278         return false;
279     }
280     if (!Option::GetOptionValue(args, "--app", appPackage_)) {
281         return false;
282     }
283     std::string err = "";
284     if (allowIpc_ && !IsExistDebugByApp(appPackage_, err)) {
285         return false;
286     }
287     if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
288         return false;
289     }
290     if (!Option::GetOptionValue(args, "--clockid", clockId_)) {
291         return false;
292     }
293     if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
294         return false;
295     }
296     if (!Option::GetOptionValue(args, "-p", selectPids_)) {
297         return false;
298     }
299     inputPidTidArgs_ = selectPids_;
300     if (!Option::GetOptionValue(args, "-t", selectTids_)) {
301         return false;
302     }
303     inputPidTidArgs_.insert(inputPidTidArgs_.end(), selectTids_.begin(), selectTids_.end());
304     if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
305         return false;
306     }
307     if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
308         return false;
309     }
310     if (!GetSpeOptions()) {
311         return false;
312     }
313     if (!Option::GetOptionValue(args, "-s", callStackType_)) {
314         return false;
315     }
316     if (!Option::GetOptionValue(args, "--kernel-callchain", kernelCallChain_)) {
317         return false;
318     }
319     if (!Option::GetOptionValue(args, "--callchain-useronly", callChainUserOnly_)) {
320         return false;
321     }
322     if (!Option::GetOptionValue(args, "--exclude-tid", excludeTidArgs_)) {
323         return false;
324     }
325     if (!Option::GetOptionValue(args, "--exclude-thread", excludeThreadNameArgs_)) {
326         return false;
327     }
328     if (!Option::GetOptionValue(args, "--restart", restart_)) {
329         return false;
330     }
331     std::vector<std::string> callStackType = {};
332     if (!Option::GetOptionValue(args, "--call-stack", callStackType)) {
333         return false;
334     }
335     if (!callStackType_.empty()) {
336         if (!callStackType.empty()) {
337             printf("'-s %s --call-stack %s' option usage error, please check usage.\n",
338                 VectorToString(callStackType_).c_str(), VectorToString(callStackType).c_str());
339             return false;
340         }
341     } else {
342         callStackType_ = callStackType;
343     }
344     if (!Option::GetOptionValue(args, "--data-limit", strLimit_)) {
345         return false;
346     }
347     if (!Option::GetOptionValue(args, "-j", vecBranchFilters_)) {
348         return false;
349     }
350     if (!Option::GetOptionValue(args, "--pipe_input", clientPipeInput_)) {
351         return false;
352     }
353     if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) {
354         return false;
355     }
356     if (clientPipeInput_ != -1 && clientPipeOutput_ != -1) {
357         isHiperfClient_ = true;
358     }
359     if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) {
360         return false;
361     }
362     if (!Option::GetOptionValue(args, "--cmdline-size", cmdlinesSize_)) {
363         return false;
364     }
365     if (!Option::GetOptionValue(args, "--report", report_)) {
366         return false;
367     }
368     if (!Option::GetOptionValue(args, "--backtrack", backtrack_)) {
369         return false;
370     }
371     if (!Option::GetOptionValue(args, "--backtrack-sec", backtrackTime_)) {
372         return false;
373     }
374     if (!Option::GetOptionValue(args, "--exclude-process", excludeProcessNameArgs_)) {
375         return false;
376     }
377     if (targetSystemWide_ && dedupStack_) {
378         printf("-a option is conflict with --dedup_stack.\n");
379         return false;
380     }
381     CHECK_TRUE(Option::GetOptionTrackedCommand(args, trackedCommand_), false, 0, "");
382     CHECK_TRUE(args.empty(), false, LOG_TYPE_PRINTF,
383                "'%s' option usage error, please check usage.\n", VectorToString(args).c_str());
384     return true;
385 }
386 
GetOptionFrequencyAndPeriod(std::vector<std::string> & args)387 bool SubCommandRecord::GetOptionFrequencyAndPeriod(std::vector<std::string> &args)
388 {
389     if (Option::FindOption(args, "-f") != args.end()) {
390         if (!Option::GetOptionValue(args, "-f", frequency_)) {
391             return false;
392         }
393         if (frequency_ < MIN_SAMPLE_FREQUENCY || frequency_ > MAX_SAMPLE_FREQUENCY) {
394             printf("Invalid -f value '%d', frequency should be in %d~%d \n", frequency_,
395                    MIN_SAMPLE_FREQUENCY, MAX_SAMPLE_FREQUENCY);
396             return false;
397         }
398     }
399     if (Option::FindOption(args, "--period") != args.end()) {
400         if (frequency_ != 0) {
401             printf("option -f and --period is conflict, please check usage\n");
402             return false;
403         }
404         if (!Option::GetOptionValue(args, "--period", period_)) {
405             return false;
406         }
407         if (period_ <= 0) {
408             printf("Invalid --period value '%d', period should be greater than 0\n", period_);
409             return false;
410         }
411     }
412     return true;
413 }
414 
CheckDataLimitOption()415 bool SubCommandRecord::CheckDataLimitOption()
416 {
417     if (!strLimit_.empty()) {
418         if (!ParseDataLimitOption(strLimit_)) {
419             printf("Invalid --data-limit value %s\n", strLimit_.c_str());
420             return false;
421         }
422     }
423     return true;
424 }
425 
CheckSelectCpuPidOption()426 bool SubCommandRecord::CheckSelectCpuPidOption()
427 {
428     if (!selectCpus_.empty()) {
429         int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
430         if (cpuCount == -1) {
431             printf("sysconf failed.\n");
432             return false;
433         }
434         int maxCpuid = cpuCount - 1;
435         for (auto cpu : selectCpus_) {
436             if (cpu < 0 || cpu > maxCpuid) {
437                 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
438                 return false;
439             }
440         }
441     }
442 
443     if (!selectPids_.empty()) {
444         for (auto pid : selectPids_) {
445             if (pid <= 0) {
446                 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
447                 return false;
448             }
449         }
450     }
451     if (!selectTids_.empty()) {
452         for (auto tid : selectTids_) {
453             if (tid <= 0) {
454                 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
455                 return false;
456             }
457         }
458     }
459     return true;
460 }
461 
CheckArgsRange()462 bool SubCommandRecord::CheckArgsRange()
463 {
464     if (CheckOutOfRange<float>(timeStopSec_, MIN_STOP_SECONDS, MAX_STOP_SECONDS)) {
465         printf("Invalid -d value '%.3f', the seconds should be in %.3f~%.3f  \n", timeStopSec_,
466                MIN_STOP_SECONDS, MAX_STOP_SECONDS);
467         return false;
468     }
469     if (CheckOutOfRange<int>(cpuPercent_, MIN_CPU_PERCENT, MAX_CPU_PERCENT)) {
470         printf("Invalid --cpu-limit value '%d', CPU percent should be in %d~%d \n", cpuPercent_,
471                MIN_CPU_PERCENT, MAX_CPU_PERCENT);
472         return false;
473     }
474     if (CheckOutOfRange<int>(checkAppMs_, MIN_CHECK_APP_MS, MAX_CHECK_APP_MS)) {
475         printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
476                MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
477         return false;
478     }
479     if (CheckOutOfRange<uint64_t>(backtrackTime_, MIN_BACKTRACK_TIME_SEC, MAX_BACKTRACK_TIME_SEC)) {
480         printf("Invalid --backtrack-sec value '%" PRIu64 " ', value should be in %" PRIu64 "~%" PRIu64 " \n",
481                backtrackTime_, MIN_BACKTRACK_TIME_SEC, MAX_BACKTRACK_TIME_SEC);
482         return false;
483     }
484     if (CheckOutOfRange<int>(mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE) ||
485         !PowerOfTwo(mmapPages_)) {
486         printf("Invalid -m value '%d', value should be in %d~%d and must be a power of two \n",
487                mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE);
488         return false;
489     }
490     if (CheckOutOfRange<int>(cmdlinesSize_, MIN_SAVED_CMDLINES_SIZE, MAX_SAVED_CMDLINES_SIZE) ||
491         !PowerOfTwo(cmdlinesSize_)) {
492         printf("Invalid --cmdline-size value '%d', value should be in %d~%d and must be a power of two \n",
493                cmdlinesSize_, MIN_SAVED_CMDLINES_SIZE, MAX_SAVED_CMDLINES_SIZE);
494         return false;
495     }
496     if (!clockId_.empty() && GetClockId(clockId_) == -1) {
497         printf("Invalid --clockid value %s\n", clockId_.c_str());
498         return false;
499     }
500     if (!IsValidOutPath(outputFilename_)) {
501         printf("Invalid output file path, permission denied\n");
502         return false;
503     }
504     return true;
505 }
506 
CheckExcludeArgs()507 bool SubCommandRecord::CheckExcludeArgs()
508 {
509     if (targetSystemWide_ && !excludeTidArgs_.empty()) {
510         printf("-a option is conflict with --exclude-tid.\n");
511         return false;
512     }
513     if (targetSystemWide_ && !excludeThreadNameArgs_.empty()) {
514         printf("-a option is conflict with --exclude-thread.\n");
515         return false;
516     }
517     if (!targetSystemWide_ && !excludeProcessNameArgs_.empty()) {
518         printf("--exclude-process must be used with -a.\n");
519         return false;
520     }
521     if (!targetSystemWide_ && excludeHiperf_) {
522         printf("--exclude-hiperf must be used with -a\n");
523         return false;
524     }
525     return true;
526 }
527 
CheckOptions()528 bool SubCommandRecord::CheckOptions()
529 {
530     if (!CheckArgsRange()) {
531         return false;
532     }
533     if (!CheckExcludeArgs()) {
534         return false;
535     }
536     if (!CheckDataLimitOption()) {
537         return false;
538     }
539     if (!ParseCallStackOption(callStackType_)) {
540         return false;
541     }
542     if (!ParseBranchSampleType(vecBranchFilters_)) {
543         return false;
544     }
545     if (!CheckSelectCpuPidOption()) {
546         return false;
547     }
548     if (!ParseControlCmd(controlCmd_)) {
549         return false;
550     }
551     if (!CheckTargetProcessOptions()) {
552         return false;
553     }
554     std::string err = "";
555     if (allowIpc_ && !IsExistDebugByPid(inputPidTidArgs_, err)) {
556         return false;
557     }
558     if (!CheckReportOption()) {
559         return false;
560     }
561     if (!CheckBacktrackOption()) {
562         return false;
563     }
564     if (!CheckSpeOption()) {
565         return false;
566     }
567     return true;
568 }
569 
570 #ifdef CONFIG_HAS_CCM
GetMmapPagesCfg()571 void SubCommandRecord::GetMmapPagesCfg()
572 {
573     size_t tmpPages = 0;
574     if (GetCfgValue(PRODUCT_CONFIG_PATH, CFG_MAP_PAGES, tmpPages)) {
575         int tmpValue = static_cast<int>(tmpPages);
576         if (CheckOutOfRange<int>(tmpValue, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE) || !PowerOfTwo(tmpValue)) {
577             HIPERF_HILOGE(MODULE_DEFAULT, "GetCfgValue %{public}s faile, %{public}d out of range",
578                           CFG_MAP_PAGES, tmpValue);
579         } else {
580             mmapPages_ = tmpValue;
581             HIPERF_HILOGI(MODULE_DEFAULT, "GetCfgValue %{public}s : %{public}d", CFG_MAP_PAGES, mmapPages_);
582         }
583     }
584 }
585 #endif
586 
ParseOption(std::vector<std::string> & args)587 bool SubCommandRecord::ParseOption(std::vector<std::string> &args)
588 {
589 #ifdef CONFIG_HAS_CCM
590     GetMmapPagesCfg();
591 #endif
592     if (!GetOptions(args)) {
593         return false;
594     }
595     CHECK_TRUE(args.empty(), false, LOG_TYPE_PRINTF, "unknown option %s\n", args.begin()->c_str());
596     if (controlCmd_.empty()) {
597         if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
598             return false;
599         }
600     }
601     return CheckOptions();
602 }
603 
CheckTargetProcessOptions()604 bool SubCommandRecord::CheckTargetProcessOptions()
605 {
606     bool hasTarget = false;
607     if (targetSystemWide_) {
608         hasTarget = true;
609     }
610     if (!selectPids_.empty() || !selectTids_.empty()) {
611         CHECK_TRUE(!hasTarget, false, LOG_TYPE_PRINTF,
612                    "-p/-t %s options conflict, please check usage\n", VectorToString(selectPids_).c_str());
613         hasTarget = true;
614     }
615     if (!trackedCommand_.empty()) {
616         if (hasTarget) {
617             printf("%s options conflict, please check usage\n",
618                    VectorToString(trackedCommand_).c_str());
619             return false;
620         }
621         if (!IsRoot()) {
622             printf("%s options needs root privilege, please check usage\n",
623                    VectorToString(trackedCommand_).c_str());
624             return false;
625         }
626         hasTarget = true;
627     }
628     if (appPackage_ != "") {
629         if (hasTarget) {
630             printf("--app %s options conflict, please check usage\n", appPackage_.c_str());
631             return false;
632         }
633         hasTarget = true;
634     }
635     if (!hasTarget && (controlCmd_.empty() || controlCmd_ == CONTROL_CMD_PREPARE)) {
636         printf("please select a target process\n");
637         return false;
638     }
639     if (controlCmd_ == CONTROL_CMD_PREPARE) {
640         CHECK_TRUE(CheckAppRestart(), false, 0, "");
641     }
642     return IsPidAndTidExist();
643 }
644 
CheckTargetPids()645 bool SubCommandRecord::CheckTargetPids()
646 {
647     if (!IsAppRunning()) {
648         return false;
649     }
650     if (!selectPids_.empty()) {
651         for (auto pid : selectPids_) {
652             auto tids = GetSubthreadIDs(pid);
653             if (!tids.empty()) {
654                 selectTids_.insert(selectTids_.end(), tids.begin(), tids.end());
655                 mapPids_[pid] = tids;
656             }
657         }
658     }
659 
660     CollectExcludeThread();
661     if (!SubCommand::HandleSubCommandExclude(excludeTidArgs_, excludeThreadNameArgs_, selectTids_)) {
662         return false;
663     }
664     selectPids_.insert(selectPids_.end(), selectTids_.begin(), selectTids_.end());
665 
666     return true;
667 }
668 
IsPidAndTidExist()669 bool SubCommandRecord::IsPidAndTidExist()
670 {
671     for (auto pid : selectPids_) {
672         if (!IsDir("/proc/" + std::to_string(pid))) {
673             printf("not exist pid %d\n", pid);
674             return false;
675         }
676     }
677     for (auto tid : selectTids_) {
678         if (!IsDir("/proc/" + std::to_string(tid))) {
679             printf("not exist tid %d\n", tid);
680             return false;
681         }
682     }
683     return true;
684 }
685 
CheckReportOption()686 bool SubCommandRecord::CheckReportOption()
687 {
688     if (targetSystemWide_ && report_) {
689         printf("--report options conflict, please check usage\n");
690         return false;
691     }
692     return true;
693 }
694 
CheckBacktrackOption()695 bool SubCommandRecord::CheckBacktrackOption()
696 {
697     CHECK_TRUE(backtrack_, true, 0, "");
698     if (controlCmd_.empty() && (clientPipeInput_ == -1)) {
699         printf("--backtrack must be used with --control\n");
700         return false;
701     }
702     CHECK_TRUE(!clockId_.empty(), true, 0, "");
703     if (GetClockId(clockId_) != CLOCK_BOOTTIME && GetClockId(clockId_) != CLOCK_MONOTONIC &&
704         GetClockId(clockId_) != CLOCK_MONOTONIC_RAW) {
705         printf("--backtrack not support the clockid\n");
706         return false;
707     }
708     return true;
709 }
710 
CheckSpeOption()711 bool SubCommandRecord::CheckSpeOption()
712 {
713     constexpr uint64_t disable = 0;
714     constexpr uint64_t enable  = 1;
715     const std::vector<std::string> optionNames = {"ts_enable", "pa_enable", "pct_enable",
716                                                   "jitter", "branch_filter", "load_filter",
717                                                   "store_filter"};
718     for (const auto &optionName : optionNames) {
719         if (CheckOutOfRange<uint64_t>(speOptMap_[optionName], disable, enable)) {
720             printf("Invalid %s value '%" PRIu64 "', value should be 0 or 1\n",
721                    optionName.c_str(), speOptMap_[optionName]);
722             return false;
723         }
724     }
725     return true;
726 }
727 
MsgPrintAndTrans(const bool isTrans,const std::string & msg)728 void SubCommandRecord::MsgPrintAndTrans(const bool isTrans, const std::string& msg)
729 {
730     if (isTrans && controlCmd_ == CONTROL_CMD_PREPARE) {
731         ChildResponseToMain(msg);
732     }
733     printf("%s", msg.c_str());
734 }
735 
IsAppRestarted()736 bool SubCommandRecord::IsAppRestarted()
737 {
738     std::set<pid_t> oldPids {};
739     std::set<pid_t> newPids {};
740     std::vector<pid_t> intersection;
741     const auto startTime = steady_clock::now();
742     const auto endTime = startTime + std::chrono::seconds(CHECK_TIMEOUT);
743     HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect oldpids begin");
744     CollectPidsByAppname(oldPids, appPackage_);
745     for (auto it = oldPids.begin(); it != oldPids.end(); it++) {
746         HIPERF_HILOGI(MODULE_DEFAULT, "[IsAppRestarted] get oldpids %{public}d for app",
747             *it);
748     }
749     HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect oldpids finish");
750     std::string info = "please restart " + appPackage_ + " for profiling within 30 seconds\n";
751     MsgPrintAndTrans(true, info);
752     do {
753         HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect newPids begin");
754         CollectPidsByAppname(newPids, appPackage_);
755         for (auto it = newPids.begin(); it != newPids.end(); it++) {
756             HIPERF_HILOGI(MODULE_DEFAULT, "[IsAppRestarted] get newPids %{public}d for app",
757                 *it);
758         }
759         HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect newPids finish");
760         std::set_intersection(oldPids.begin(), oldPids.end(),
761             newPids.begin(), newPids.end(), std::back_insert_iterator(intersection));
762         // app names are same, no intersection, means app restarted
763         CHECK_TRUE(!intersection.empty(), true, 0, "");
764         intersection.clear();
765         newPids.clear();
766         std::this_thread::sleep_for(milliseconds(CHECK_FREQUENCY));
767     } while (steady_clock::now() < endTime && !g_callStop.load());
768     std::string err = "app " + appPackage_ + " was not stopped within 30 seconds\n";
769     MsgPrintAndTrans(!g_callStop.load(), err);
770     return false;
771 }
772 
CheckAppRestart()773 bool SubCommandRecord::CheckAppRestart()
774 {
775     if (!restart_) {
776         return true;
777     }
778     if (appPackage_.empty()) {
779         printf("to detect the performance of application startup, --app option must be given\n");
780         return false;
781     }
782     if (targetSystemWide_ || !selectPids_.empty()) {
783         printf("option --restart and -p/-a is conflict, please check usage\n");
784         return false;
785     }
786     return true;
787 }
788 
GetPidFromAppPackage(const pid_t oldPid,const uint64_t waitAppTimeOut)789 pid_t SubCommandRecord::GetPidFromAppPackage(const pid_t oldPid, const uint64_t waitAppTimeOut)
790 {
791     pid_t res {-1};
792     const std::string basePath {"/proc/"};
793     const std::string cmdline {"/cmdline"};
794     const auto startTime = steady_clock::now();
795     const auto endTime = startTime + std::chrono::seconds(waitAppTimeOut);
796     do {
797         std::vector<std::string> subDirs = GetSubDirs(basePath);
798         for (const auto &subDir : subDirs) {
799             if (!IsDigits(subDir)) {
800                 continue;
801             }
802             std::string fileName {basePath + subDir + cmdline};
803             if (IsSameCommand(ReadFileToString(fileName), appPackage_)) {
804                 res = static_cast<pid_t>(std::stoul(subDir, nullptr));
805                 HLOGD("[GetAppPackagePid]: get appPid for %s is %d", appPackage_.c_str(), res);
806                 HIPERF_HILOGD(MODULE_DEFAULT, "[GetAppPackagePid] get appPid %{public}d for app",
807                     res);
808                 return res;
809             }
810         }
811         std::this_thread::sleep_for(milliseconds(checkAppMs_));
812     } while (steady_clock::now() < endTime && !g_callStop.load());
813     return res;
814 }
815 
IsAppRunning()816 bool SubCommandRecord::IsAppRunning()
817 {
818     if (!appPackage_.empty()) {
819         pid_t appPid = GetPidFromAppPackage(-1, waitAppRunCheckTimeOut);
820         if (appPid <= 0) {
821             std::string err = "app " +  appPackage_ + " not running\n";
822             MsgPrintAndTrans(!g_callStop.load(), err);
823             return false;
824         }
825         HLOGD("[CheckAppIsRunning] get appPid %d for app %s", appPid, appPackage_.c_str());
826         HIPERF_HILOGD(MODULE_DEFAULT, "[CheckAppIsRunning] get appPid %{public}d for app",
827             appPid);
828         selectPids_.push_back(appPid);
829     }
830     return true;
831 }
832 
ParseDataLimitOption(const std::string & str)833 bool SubCommandRecord::ParseDataLimitOption(const std::string &str)
834 {
835     uint unit = 1;
836     char c = str.at(str.size() >= 1 ? str.size() - 1 : 0);
837     if (c == 'K' || c == 'k') {
838         unit = KILO;
839     } else if (c == 'm' || c == 'M') {
840         unit = KILO * KILO;
841     } else if (c == 'g' || c == 'G') {
842         unit = KILO * KILO * KILO;
843     } else {
844         return false;
845     }
846 
847     std::string numStr = str.substr(0, str.size() >= 1 ? str.size() - 1 : 0);
848     unsigned long size = 0;
849     char *endPtr = nullptr;
850     errno = 0;
851     size = std::strtoul(numStr.c_str(), &endPtr, 10); // 10 : decimal scale
852     if (endPtr == numStr.c_str() || *endPtr != '\0' || errno != 0 || size == 0) {
853         HLOGE("num string convert to size failed, numStr: %s", numStr.c_str());
854         return false;
855     }
856 
857     dataSizeLimit_ = size * unit;
858 
859     return true;
860 }
861 
ParseCallStackOption(const std::vector<std::string> & callStackType)862 bool SubCommandRecord::ParseCallStackOption(const std::vector<std::string> &callStackType)
863 {
864     if (callStackType.empty()) {
865         return true;
866     } else if (callStackType[0] == "fp") {
867         if (callStackType.size() != 1) {
868             printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str());
869             return false;
870         }
871         isCallStackFp_ = true;
872     } else if (callStackType[0] == "dwarf") {
873         if (callStackType.size() > MAX_DWARF_CALL_CHAIN) {
874             printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str());
875             return false;
876         } else if (callStackType.size() == MAX_DWARF_CALL_CHAIN) {
877             char *endPtr = nullptr;
878             errno = 0;
879             unsigned long num = 0;
880             num = std::strtoul(callStackType.at(1).c_str(), &endPtr, 10); // 10 : decimal scale
881             if (endPtr == callStackType.at(1).c_str() || *endPtr != '\0' || errno > 0 || num > UINT_MAX) {
882                 printf("Invalid -s value, dwarf stack size, '%s' is illegal.\n",
883                        callStackType.at(1).c_str());
884                 return false;
885             }
886             callStackDwarfSize_ = static_cast<uint32_t>(num);
887 
888             if (callStackDwarfSize_ < MIN_SAMPLE_STACK_SIZE) {
889                 printf("Invalid -s value, dwarf stack size, '%s' is too small.\n",
890                        callStackType.at(1).c_str());
891                 return false;
892             }
893             if (callStackDwarfSize_ > MAX_SAMPLE_STACK_SIZE) {
894                 printf("Invalid -s value, dwarf stack size, '%s' is bigger than max value %u.\n",
895                        callStackType.at(1).c_str(), MAX_SAMPLE_STACK_SIZE);
896                 return false;
897             }
898             if ((callStackDwarfSize_ & MASK_ALIGNED_8) != 0) {
899                 printf("Invalid -s value, dwarf stack size, '%s' is not 8 byte aligned.\n",
900                        callStackType.at(1).c_str());
901                 return false;
902             }
903         }
904         isCallStackDwarf_ = true;
905         SymbolsFile::needParseJsFunc_ = true; // only in record and dwarf mode need to parse
906     } else {
907         printf("Invalid -s value '%s'.\n", callStackType.at(0).c_str());
908         return false;
909     }
910     return true;
911 }
912 
ParseBranchSampleType(const std::vector<std::string> & vecBranchSampleTypes)913 bool SubCommandRecord::ParseBranchSampleType(const std::vector<std::string> &vecBranchSampleTypes)
914 {
915     if (!vecBranchSampleTypes.empty()) {
916         for (auto &item : vecBranchSampleTypes) {
917             uint64_t type = GetBranchSampleType(item);
918             if (type != 0) {
919                 branchSampleType_ |= type;
920             } else {
921                 printf("Invalid -j value '%s'\n", item.c_str());
922                 return false;
923             }
924         }
925         if ((branchSampleType_ & TYPE_PERF_SAMPLE_BRANCH) == 0) {
926             printf("Invalid -j value, requires at least one of "
927                    "any, any_call, any_ret, ind_call, ind_jmp, cond, call.\n");
928             return false;
929         }
930     }
931     return true;
932 }
933 
ParseControlCmd(const std::string cmd)934 bool SubCommandRecord::ParseControlCmd(const std::string cmd)
935 {
936     if (cmd.empty() || cmd == CONTROL_CMD_PREPARE || cmd == CONTROL_CMD_START || cmd == CONTROL_CMD_PAUSE ||
937         cmd == CONTROL_CMD_RESUME || cmd == CONTROL_CMD_STOP || cmd == CONTROL_CMD_OUTPUT) {
938         return true;
939     }
940 
941     printf("Invalid --control %s option, command should be: prepare, start, pause, resume, output, stop.\n",
942            cmd.c_str());
943     return false;
944 }
945 
SetPerfLimit(const std::string & file,int value,std::function<bool (int,int)> const & cmp,const std::string & param)946 bool SubCommandRecord::SetPerfLimit(const std::string& file, int value, std::function<bool (int, int)> const& cmp,
947     const std::string& param)
948 {
949     int oldValue = 0;
950     CHECK_TRUE(ReadIntFromProcFile(file, oldValue), false, LOG_TYPE_PRINTF, "read %s fail.\n", file.c_str());
951 
952     if (cmp(oldValue, value)) {
953         HLOGI("cmp return true.");
954         return true;
955     }
956 
957     if (IsRoot()) {
958         bool ret = WriteIntToProcFile(file, value);
959         if (!ret) {
960             printf("please set %s to %d manually if perf limit is wanted.\n", file.c_str(), value);
961         }
962     }
963 
964     CHECK_TRUE(OHOS::system::SetParameter(param, std::to_string(value)), false, LOG_TYPE_PRINTF,
965                "set parameter %s fail.\n", param.c_str());
966     isNeedSetPerfHarden_ = true;
967     return true;
968 }
969 
SetPerfCpuMaxPercent()970 bool SubCommandRecord::SetPerfCpuMaxPercent()
971 {
972     auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
973     return SetPerfLimit(PERF_CPU_TIME_MAX_PERCENT, cpuPercent_, cmp, "hiviewdfx.hiperf.perf_cpu_time_max_percent");
974 }
975 
SetPerfMaxSampleRate()976 bool SubCommandRecord::SetPerfMaxSampleRate()
977 {
978     auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
979     int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY;
980     int maxRate = 0;
981     CHECK_TRUE(ReadIntFromProcFile(PERF_EVENT_MAX_SAMPLE_RATE, maxRate), false, LOG_TYPE_PRINTF,
982                "read %s fail.\n", PERF_EVENT_MAX_SAMPLE_RATE.c_str());
983     if (maxRate > frequency) {
984         return true;
985     }
986     int newRate = frequency > static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE) ? frequency :
987                   static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE);
988     return SetPerfLimit(PERF_EVENT_MAX_SAMPLE_RATE, newRate, cmp,
989                         "hiviewdfx.hiperf.perf_event_max_sample_rate");
990 }
991 
SetPerfEventMlock()992 bool SubCommandRecord::SetPerfEventMlock()
993 {
994     auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
995     int mlock_kb = sysconf(_SC_NPROCESSORS_CONF) * (mmapPages_ + 1) * 4;
996     return SetPerfLimit(PERF_EVENT_MLOCK_KB, mlock_kb, cmp, "hiviewdfx.hiperf.perf_event_mlock_kb");
997 }
998 
SetPerfHarden()999 bool SubCommandRecord::SetPerfHarden()
1000 {
1001     if (!isNeedSetPerfHarden_) {
1002         return true;
1003     }
1004 
1005     std::string perfHarden = OHOS::system::GetParameter(PERF_DISABLE_PARAM, "1");
1006     if (perfHarden == "1") {
1007         CHECK_TRUE(OHOS::system::SetParameter(PERF_DISABLE_PARAM, "0"), false, LOG_TYPE_PRINTF,
1008                    "set parameter security.perf_harden to 0 fail.");
1009     }
1010 
1011     CHECK_TRUE(OHOS::system::SetParameter(PERF_DISABLE_PARAM, "1"), false, LOG_TYPE_PRINTF,
1012                "set parameter security.perf_harden to 1 fail.");
1013     return true;
1014 }
1015 
TraceOffCpu()1016 bool SubCommandRecord::TraceOffCpu()
1017 {
1018     // whether system support sched_switch event
1019     int enable = -1;
1020     std::string node = SCHED_SWITCH;
1021     const std::string nodeDebug = SCHED_SWITCH_DEBUG;
1022     CHECK_TRUE(ReadIntFromProcFile(node.c_str(), enable) || ReadIntFromProcFile(nodeDebug.c_str(), enable),
1023                false, LOG_TYPE_PRINTF, "Cannot trace off CPU, event sched:sched_switch is not available (%s or %s)\n",
1024                node.c_str(), nodeDebug.c_str());
1025 
1026     return true;
1027 }
1028 
SetSavedCmdlinesSize()1029 void SubCommandRecord::SetSavedCmdlinesSize()
1030 {
1031     if (!ReadIntFromProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) {
1032         printf("Failed to read from %s.\n", SAVED_CMDLINES_SIZE.c_str());
1033     }
1034     if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, cmdlinesSize_)) {
1035         printf("Failed to write size:%d to %s.\n", cmdlinesSize_, SAVED_CMDLINES_SIZE.c_str());
1036     }
1037 }
1038 
RecoverSavedCmdlinesSize()1039 void SubCommandRecord::RecoverSavedCmdlinesSize()
1040 {
1041     CHECK_TRUE(oldCmdlinesSize_ != 0, NO_RETVAL, 0, "");
1042     if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) {
1043         printf("Failed to recover value of %s.\n", SAVED_CMDLINES_SIZE.c_str());
1044     }
1045 }
1046 
PreparePerfEvent()1047 bool SubCommandRecord::PreparePerfEvent()
1048 {
1049     // we need to notify perfEvents_ sampling mode by SetRecordCallBack first
1050     auto processRecord = [this](PerfEventRecord& record) -> bool {
1051         return this->ProcessRecord(record);
1052     };
1053     perfEvents_.SetRecordCallBack(processRecord);
1054 
1055     if (selectEvents_.size() > 0 && selectEvents_[0] == "arm_spe_0") {
1056         if (!IsRoot()) {
1057             printf("%s options needs root privilege, please check usage\n", selectEvents_[0].c_str());
1058             return false;
1059         }
1060         selectEvents_.insert(selectEvents_.begin(), "sw-dummy");
1061         perfEvents_.isSpe_ = true;
1062         perfEvents_.SetConfig(speOptMap_);
1063         isSpe_ = true;
1064     }
1065 
1066     perfEvents_.SetCpu(selectCpus_);
1067     perfEvents_.SetPid(selectPids_); // Tids has insert Pids in CheckTargetProcessOptions()
1068 
1069     perfEvents_.SetSystemTarget(targetSystemWide_);
1070     perfEvents_.SetTimeOut(timeStopSec_);
1071     perfEvents_.SetVerboseReport(verboseReport_);
1072     perfEvents_.SetMmapPages(mmapPages_);
1073     if (isCallStackFp_) {
1074         perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::FP);
1075     } else if (isCallStackDwarf_) {
1076         perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::DWARF);
1077         perfEvents_.SetDwarfSampleStackSize(callStackDwarfSize_);
1078     }
1079     if (!perfEvents_.SetBranchSampleType(branchSampleType_)) {
1080         printf("branch sample %s is not supported\n", VectorToString(vecBranchFilters_).c_str());
1081         HLOGE("Fail to SetBranchSampleType %" PRIx64 "", branchSampleType_);
1082         HIPERF_HILOGE(MODULE_DEFAULT, "[PreparePerfEvent] Fail to SetBranchSampleType %{public}" PRIx64 "",
1083             branchSampleType_);
1084         return false;
1085     }
1086     if (!clockId_.empty()) {
1087         perfEvents_.SetClockId(GetClockId(clockId_));
1088     }
1089 
1090     if (frequency_ > 0) {
1091         perfEvents_.SetSampleFrequency(frequency_);
1092     } else if (period_ > 0) {
1093         perfEvents_.SetSamplePeriod(period_);
1094     }
1095 
1096     perfEvents_.SetBackTrack(backtrack_);
1097     perfEvents_.SetBackTrackTime(backtrackTime_);
1098 
1099     perfEvents_.SetInherit(!noInherit_);
1100     perfEvents_.SetTrackedCommand(trackedCommand_);
1101 
1102     // set default sample event
1103     if (selectEvents_.empty() && selectGroups_.empty()) {
1104         selectEvents_.push_back("hw-cpu-cycles");
1105     }
1106 
1107     CHECK_TRUE(perfEvents_.AddEvents(selectEvents_), false, 1, "Fail to AddEvents events");
1108     for (auto &group : selectGroups_) {
1109         CHECK_TRUE(perfEvents_.AddEvents(group, true), false, 1, "Fail to AddEvents groups");
1110     }
1111     // cpu off add after default event (we need both sched_switch and user selected events)
1112     if (offCPU_) {
1113         CHECK_TRUE(std::find(selectEvents_.begin(), selectEvents_.end(), "sched_switch") == selectEvents_.end(),
1114                    false, LOG_TYPE_PRINTF, "--offcpu is not supported event sched_switch\n");
1115         // insert a sched_switch event to trace offcpu event
1116         CHECK_TRUE(perfEvents_.AddOffCpuEvent(), false, 1, "Fail to AddEOffCpuvent");
1117     }
1118 
1119     return true;
1120 }
1121 
PrepareSysKernel()1122 bool SubCommandRecord::PrepareSysKernel()
1123 {
1124     SetHM();
1125     SetSavedCmdlinesSize();
1126     CHECK_TRUE(SetPerfMaxSampleRate(), false, 1, "Fail to call SetPerfMaxSampleRate(%d)", frequency_);
1127 
1128     CHECK_TRUE(SetPerfCpuMaxPercent(), false, 1, "Fail to set perf event cpu limit to %d\n", cpuPercent_);
1129 
1130     CHECK_TRUE(SetPerfEventMlock(), false, 1, "Fail to set perf event mlock limit\n");
1131 
1132     CHECK_TRUE(SetPerfHarden(), false, 1, "Fail to set perf event harden\n");
1133 
1134     CHECK_TRUE(!offCPU_ || TraceOffCpu(), false, 1, "Fail to TraceOffCpu");
1135 
1136     return true;
1137 }
1138 
PrepareKernelMaps()1139 void SubCommandRecord::PrepareKernelMaps()
1140 {
1141     // load vsdo first
1142     virtualRuntime_.LoadVdso();
1143 
1144     if (!callChainUserOnly_) {
1145         // prepare from kernel and ko
1146         virtualRuntime_.SetNeedKernelCallChain(!callChainUserOnly_);
1147         virtualRuntime_.UpdateKernelSpaceMaps();
1148         if (isRoot_) {
1149             virtualRuntime_.UpdateKernelModulesSpaceMaps();
1150         }
1151         if (isHM_) {
1152             virtualRuntime_.UpdateServiceSpaceMaps();
1153         }
1154     }
1155 
1156     if (isHM_) {
1157         virtualRuntime_.UpdateDevhostSpaceMaps();
1158     }
1159 }
1160 
PrepareVirtualRuntime()1161 bool SubCommandRecord::PrepareVirtualRuntime()
1162 {
1163     auto saveRecord = [this](PerfEventRecord& record) -> bool {
1164         if (!isRoot_) {
1165             UpdateDevHostMaps(record);
1166         }
1167         return this->SaveRecord(record);
1168     };
1169     virtualRuntime_.SetRecordMode(saveRecord);
1170 
1171     // do some config for virtualRuntime_
1172     virtualRuntime_.SetCallStackExpend(disableCallstackExpend_ ? 0 : 1);
1173     // these is same for virtual runtime
1174     virtualRuntime_.SetDisableUnwind(disableUnwind_ || delayUnwind_);
1175     virtualRuntime_.EnableDebugInfoSymbolic(enableDebugInfoSymbolic_);
1176     if (!symbolDir_.empty()) {
1177         if (!virtualRuntime_.SetSymbolsPaths(symbolDir_)) {
1178             printf("Failed to set symbol path(%s)\n", VectorToString(symbolDir_).c_str());
1179             return false;
1180         }
1181     }
1182 
1183     PrepareKernelMaps();
1184     if (dedupStack_) {
1185         virtualRuntime_.SetDedupStack();
1186         auto collectSymbol = [this](PerfRecordSample *sample) {
1187             this->CollectSymbol(std::move(sample));
1188         };
1189         virtualRuntime_.SetCollectSymbolCallBack(collectSymbol);
1190     }
1191     return true;
1192 }
1193 
WriteCommEventBeforeSampling()1194 void SubCommandRecord::WriteCommEventBeforeSampling()
1195 {
1196     CHECK_TRUE(!restart_, NO_RETVAL, 0, "");
1197     CHECK_TRUE(!backtrack_, NO_RETVAL, 0, "");
1198     for (auto it = mapPids_.begin(); it != mapPids_.end(); ++it) {
1199         virtualRuntime_.GetThread(it->first, it->first);
1200         for (auto tid : it->second) {
1201             virtualRuntime_.GetThread(it->first, tid);
1202         }
1203     }
1204     if (isSpe_ && mapPids_.empty() && !selectPids_.empty()) {
1205         for (auto pid : selectPids_) {
1206             virtualRuntime_.GetThread(pid, pid);
1207         }
1208     }
1209 }
1210 
ClientCommandResponse(const bool response)1211 bool SubCommandRecord::ClientCommandResponse(const bool response)
1212 {
1213     return ClientCommandResponse(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL);
1214 }
1215 
ClientCommandResponse(const std::string & str)1216 bool SubCommandRecord::ClientCommandResponse(const std::string& str)
1217 {
1218     if (!isHiperfClient_) {
1219         clientPipeOutput_ = open(fifoFileS2C_.c_str(), O_WRONLY);
1220         if (clientPipeOutput_ == -1) {
1221             char errInfo[ERRINFOLEN] = { 0 };
1222             strerror_r(errno, errInfo, ERRINFOLEN);
1223             HLOGE("open fifo file(%s) failed, errno:%d:%s", fifoFileS2C_.c_str(), errno, errInfo);
1224             HIPERF_HILOGE(MODULE_DEFAULT, "open fifo file(%{public}s) failed, errno:%{public}d:%{public}s",
1225                 fifoFileS2C_.c_str(), errno, errInfo);
1226             return false;
1227         }
1228     }
1229     ssize_t size = write(clientPipeOutput_, str.c_str(), str.size());
1230     if (size != static_cast<ssize_t>(str.size())) {
1231         char errInfo[ERRINFOLEN] = { 0 };
1232         strerror_r(errno, errInfo, ERRINFOLEN);
1233         HLOGD("Server:%s -> %d : %zd %d:%s", str.c_str(), clientPipeOutput_, size, errno, errInfo);
1234         HIPERF_HILOGE(MODULE_DEFAULT, "Server:%{public}s -> %{public}d : %{public}zd %{public}d:%{public}s",
1235             str.c_str(), clientPipeOutput_, size, errno, errInfo);
1236         return false;
1237     }
1238     return true;
1239 }
1240 
ChildResponseToMain(const bool response)1241 bool SubCommandRecord::ChildResponseToMain(const bool response)
1242 {
1243     return ChildResponseToMain(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL);
1244 }
1245 
ChildResponseToMain(const std::string & str)1246 bool SubCommandRecord::ChildResponseToMain(const std::string& str)
1247 {
1248     int tempFd = isHiperfClient_ ? clientPipeOutput_ : writeFd_;
1249     ssize_t size = write(tempFd, str.c_str(), str.size());
1250     if (size != static_cast<ssize_t>(str.size())) {
1251         char errInfo[ERRINFOLEN] = { 0 };
1252         strerror_r(errno, errInfo, ERRINFOLEN);
1253         HLOGE("write pipe failed. str:%s, size:%zd, errno:%d:%s", str.c_str(), size, errno, errInfo);
1254         HIPERF_HILOGE(MODULE_DEFAULT,
1255                       "write pipe failed. str:%{public}s, size:%{public}zd, errno:%{public}d:%{public}s",
1256                       str.c_str(), size, errno, errInfo);
1257         return false;
1258     }
1259     return true;
1260 }
1261 
MainRecvFromChild(const int fd,std::string & reply)1262 bool SubCommandRecord::MainRecvFromChild(const int fd, std::string& reply)
1263 {
1264     struct pollfd pollFd {
1265         fd, POLLIN, 0
1266     };
1267     int polled = poll(&pollFd, 1, CONTROL_WAIT_RESPONSE_TIMEOUT);
1268     reply.clear();
1269     if (polled > 0) {
1270         bool exitLoop = false;
1271         while (!exitLoop) {
1272             char c;
1273             ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1));
1274             if (result <= 0) {
1275                 HLOGD("read from pipeFd failed");
1276                 HIPERF_HILOGE(MODULE_DEFAULT, "read from pipeFd failed");
1277                 exitLoop = true;
1278             }
1279             reply.push_back(c);
1280             if (c == '\n') {
1281                 exitLoop = true;
1282             }
1283         }
1284     } else if (polled == 0) {
1285         HLOGD("wait pipeFd timeout");
1286         HIPERF_HILOGE(MODULE_DEFAULT, "wait pipeFd timeout");
1287         return false;
1288     } else {
1289         HLOGD("wait pipeFd failed");
1290         HIPERF_HILOGE(MODULE_DEFAULT, "wait pipeFd failed");
1291         return false;
1292     }
1293     return true;
1294 }
1295 
IsSamplingRunning()1296 bool SubCommandRecord::IsSamplingRunning()
1297 {
1298     constexpr int maxWaitTrackingCount = 3000 / 100; // wait 3 second
1299     int waitTrackingCount = maxWaitTrackingCount;
1300     while (!perfEvents_.IsTrackRunning()) {
1301         waitTrackingCount--;
1302         if (waitTrackingCount <= 0) {
1303             return false;
1304         }
1305         constexpr uint64_t waitTrackingSleepMs = 100;
1306         std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs));
1307     }
1308     return true;
1309 }
1310 
PreOutputRecordFile()1311 bool SubCommandRecord::PreOutputRecordFile()
1312 {
1313     if (!backtrack_) {
1314         HLOGE("not backtrack mode");
1315         return false;
1316     }
1317     if (perfEvents_.IsOutputTracking()) {
1318         HLOGE("output track is in process");
1319         return false;
1320     }
1321     if (!CreateInitRecordFile(false)) {
1322         HLOGE("create record file before output");
1323         return false;
1324     }
1325     PrepareKernelMaps();
1326     if (!perfEvents_.OutputTracking()) {
1327         HLOGE("enable output tracking failed");
1328         return false;
1329     }
1330     outputEnd_ = false;
1331     perfPipe_.SetOutPutEnd(outputEnd_);
1332     return true;
1333 }
1334 
OutputRecordFile()1335 void SubCommandRecord::OutputRecordFile()
1336 {
1337     uint32_t loopCount = 0;
1338     while (perfEvents_.IsOutputTracking()) {
1339         std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS));
1340         if (loopCount++ > MAX_SERVER_OUTPUT_WAIT_COUNT) {
1341             HLOGE("wait time out");
1342             perfEvents_.SetOutputTrackingStatus(false);
1343             break;
1344         }
1345     }
1346 
1347     if (!FinishWriteRecordFile()) {
1348         HLOGE("output record failed");
1349     }
1350     fileWriter_ = nullptr;
1351 }
1352 
PostOutputRecordFile(const bool output)1353 bool SubCommandRecord::PostOutputRecordFile(const bool output)
1354 {
1355     if (output) {
1356         OutputRecordFile();
1357     }
1358 
1359     fileWriter_ = nullptr;
1360     outputEnd_ = true;
1361     perfPipe_.SetOutPutEnd(outputEnd_);
1362     StringViewHold::Get().Clean();
1363     return true;
1364 }
1365 
InitControlCommandHandlerMap()1366 void SubCommandRecord::InitControlCommandHandlerMap()
1367 {
1368     controlCommandHandlerMap_.clear();
1369     controlCommandHandlerMap_.emplace(HiperfClient::ReplyStart, ControlCommandHandler{
1370         std::bind(&PerfEvents::EnableTracking, &perfEvents_)
1371     });
1372 
1373     controlCommandHandlerMap_.emplace(HiperfClient::ReplyCheck, ControlCommandHandler{
1374         std::bind(&SubCommandRecord::clientRunning_, this)
1375     });
1376 
1377     controlCommandHandlerMap_.emplace(HiperfClient::ReplyStop, ControlCommandHandler{
1378         std::bind(&PerfEvents::StopTracking, &perfEvents_)
1379     });
1380 
1381     controlCommandHandlerMap_.emplace(HiperfClient::ReplyPause, ControlCommandHandler{
1382         std::bind(&PerfEvents::PauseTracking, &perfEvents_)
1383     });
1384 
1385     controlCommandHandlerMap_.emplace(HiperfClient::ReplyResume, ControlCommandHandler{
1386         std::bind(&PerfEvents::ResumeTracking, &perfEvents_)
1387     });
1388 
1389     controlCommandHandlerMap_.emplace(HiperfClient::ReplyOutput, ControlCommandHandler{
1390         std::bind(&SubCommandRecord::PreOutputRecordFile, this),
1391         std::bind(&SubCommandRecord::PostOutputRecordFile, this, std::placeholders::_1)
1392     });
1393 
1394     controlCommandHandlerMap_.emplace(HiperfClient::ReplyOutputCheck, ControlCommandHandler{
1395         std::bind(&SubCommandRecord::outputEnd_, this)
1396     });
1397 }
1398 
CreateReplyThread()1399 inline void SubCommandRecord::CreateReplyThread()
1400 {
1401     replyCommandHandle_ = std::thread(&SubCommandRecord::ReplyCommandHandle, this);
1402 }
1403 
ReplyCommandHandle()1404 void SubCommandRecord::ReplyCommandHandle()
1405 {
1406     if (!IsSamplingRunning()) {
1407         HLOGI("IsSamplingRunning() return false");
1408         HIPERF_HILOGI(MODULE_DEFAULT, "IsSamplingRunning() return false");
1409         ChildResponseToMain(false);
1410         isHiperfClient_ = false;
1411         return;
1412     }
1413     ChildResponseToMain(true);
1414 }
1415 
CreateClientThread()1416 inline void SubCommandRecord::CreateClientThread()
1417 {
1418     // make a thread wait the other command
1419     clientCommandHandle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this);
1420 }
1421 
ClientCommandHandle()1422 void SubCommandRecord::ClientCommandHandle()
1423 {
1424     using namespace HiperfClient;
1425     InitControlCommandHandlerMap();
1426 
1427     bool hasRead = true;
1428     while (clientRunning_.load()) {
1429         if (isFifoServer_ && hasRead) {
1430             if (clientPipeInput_ != -1) {
1431                 // after read(), block is disabled, the poll will be waked neven if no data
1432                 close(clientPipeInput_);
1433             }
1434             clientPipeInput_ = open(fifoFileC2S_.c_str(), O_RDONLY | O_NONBLOCK);
1435         }
1436         struct pollfd pollFd {
1437             clientPipeInput_, POLLIN, 0
1438         };
1439         int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TIMEOUT.count());
1440         if (polled <= 0) {
1441             hasRead = false;
1442             continue;
1443         }
1444         hasRead = true;
1445         std::string command;
1446         while (true) {
1447             char c;
1448             ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1));
1449             if (result <= 0) {
1450                 HLOGD("server :read from pipe file failed");
1451                 HIPERF_HILOGD(MODULE_DEFAULT, "[ClientCommandHandle] server :read from pipe file failed");
1452                 break;
1453             }
1454             command.push_back(c);
1455             if (c == '\n') {
1456                 break;
1457             }
1458         }
1459         HLOGD("server:new command %s", command.c_str());
1460         HIPERF_HILOGI(MODULE_DEFAULT, "[ClientCommandHandle] server:new command : %{public}s", command.c_str());
1461         if (command.find("STOP") != std::string::npos) {
1462             HLOGD("receive sop command, set g_callStop to true");
1463             HIPERF_HILOGI(MODULE_DEFAULT, "[ClientCommandHandle] Handlereceive sop command, set g_callStop to true");
1464             g_callStop.store(true);
1465         }
1466         DispatchControlCommand(command);
1467     }
1468 }
1469 
DispatchControlCommand(const std::string & command)1470 void SubCommandRecord::DispatchControlCommand(const std::string& command)
1471 {
1472     auto it = controlCommandHandlerMap_.find(command);
1473     if (it == controlCommandHandlerMap_.end()) {
1474         return;
1475     }
1476 
1477     ControlCommandHandler& handler = it->second;
1478     bool ret = handler.preProcess();
1479     ClientCommandResponse(ret);
1480     handler.postProcess(ret);
1481 }
1482 
ProcessControl()1483 bool SubCommandRecord::ProcessControl()
1484 {
1485     if (controlCmd_.empty()) {
1486         return true;
1487     }
1488     HIPERF_HILOGI(MODULE_DEFAULT, "control cmd : %{public}s", controlCmd_.c_str());
1489     perfPipe_.SetFifoFileName(CommandType::RECORD, controlCmd_, fifoFileC2S_, fifoFileS2C_);
1490     if (controlCmd_ == CONTROL_CMD_PREPARE) {
1491         CHECK_TRUE(CreateFifoServer(), false, 0, "");
1492         return true;
1493     }
1494 
1495     isFifoClient_ = true;
1496     return perfPipe_.ProcessControlCmd();
1497 }
1498 
RemoveFifoFile()1499 void SubCommandRecord::RemoveFifoFile()
1500 {
1501     char errInfo[ERRINFOLEN] = { 0 };
1502     if (remove(fifoFileC2S_.c_str()) != 0) {
1503         strerror_r(errno, errInfo, ERRINFOLEN);
1504         HLOGE("remove %s failed, errno:(%d:%s)", fifoFileC2S_.c_str(), errno, errInfo);
1505         HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)",
1506             fifoFileC2S_.c_str(), errno, errInfo);
1507     }
1508     if (remove(fifoFileS2C_.c_str()) != 0) {
1509         strerror_r(errno, errInfo, ERRINFOLEN);
1510         HLOGE("remove %s failed, errno:(%d:%s)", fifoFileS2C_.c_str(), errno, errInfo);
1511         HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)",
1512             fifoFileS2C_.c_str(), errno, errInfo);
1513     }
1514 }
1515 
CreateFifoServer()1516 bool SubCommandRecord::CreateFifoServer()
1517 {
1518     char errInfo[ERRINFOLEN] = { 0 };
1519     if (!perfPipe_.CreateFifoFile()) {
1520         return false;
1521     }
1522 
1523     int pipeFd[2];
1524     if (pipe(pipeFd)  == -1) {
1525         strerror_r(errno, errInfo, ERRINFOLEN);
1526         HLOGE("pipe creation error, errno:(%d:%s)", errno, errInfo);
1527         HIPERF_HILOGE(MODULE_DEFAULT, "pipe creation error, errno:(%{public}d:%{public}s)", errno, errInfo);
1528         RemoveFifoFile();
1529         return false;
1530     }
1531 
1532     CheckIpcBeforeFork();
1533     pid_t pid = fork();
1534     allowIpc_ = true;
1535 
1536     if (pid == -1) {
1537         strerror_r(errno, errInfo, ERRINFOLEN);
1538         HLOGE("fork failed. %d:%s", errno, errInfo);
1539         HIPERF_HILOGE(MODULE_DEFAULT, "[CreateFifoServer] fork failed. %{public}d:%{public}s", errno, errInfo);
1540         close(pipeFd[PIPE_READ]);
1541         close(pipeFd[PIPE_WRITE]);
1542         return false;
1543     } else if (pid == 0) { // child process
1544         close(STDIN_FILENO);
1545         close(STDERR_FILENO);
1546         close(pipeFd[PIPE_READ]);
1547         writeFd_ = pipeFd[PIPE_WRITE];
1548         isFifoServer_ = true;
1549         nullFd_ = open("/dev/null", O_WRONLY);
1550         (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null
1551     } else {            // parent process
1552         close(pipeFd[PIPE_WRITE]);
1553         readFd_ = pipeFd[PIPE_READ];
1554         isFifoClient_ = true;
1555         bool isSuccess = false;
1556         bool isPrint = false;
1557         const auto startTime = steady_clock::now();
1558         const auto endTime = startTime + std::chrono::seconds(WAIT_TIMEOUT);
1559         do {
1560             std::string reply = "";
1561             bool ret = MainRecvFromChild(readFd_, reply);
1562             if (ret && reply.find("OK") != std::string::npos) {
1563                 printf("%s control hiperf sampling success.\n", restart_ ? "start" : "create");
1564                 isSuccess = true;
1565                 break;
1566             }
1567             HLOGE("reply is (%s)", reply.c_str());
1568             HIPERF_HILOGE(MODULE_DEFAULT, "reply is (%{public}s)", reply.c_str());
1569             if (ret && reply.find("FAIL") == std::string::npos) {
1570                 printf("%s", reply.c_str());
1571                 if (reply.find("debug application") != std::string::npos) {
1572                     isPrint = true;
1573                 }
1574                 continue;
1575             }
1576             if (ret && reply.find("FAIL") != std::string::npos) {
1577                 break;
1578             }
1579             if (!ret) {
1580                 isPrint = true;
1581                 break;
1582             }
1583         } while (steady_clock::now() < endTime);
1584         if (!isSuccess) {
1585             kill(pid, SIGKILL);
1586             RemoveFifoFile();
1587             if (isPrint) {
1588                 strerror_r(errno, errInfo, ERRINFOLEN);
1589                 printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo);
1590                 return false;
1591             }
1592         }
1593     }
1594     return true;
1595 }
1596 
OnSubCommand(std::vector<std::string> & args)1597 HiperfError SubCommandRecord::OnSubCommand(std::vector<std::string>& args)
1598 {
1599     HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord onSubCommand start");
1600     if (!ProcessControl()) {
1601         return HiperfError::PROCESS_CONTROL_FAIL;
1602     } else if (isFifoClient_) {
1603         return HiperfError::NO_ERR;
1604     }
1605     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] ProcessControl finish");
1606     if (controlCmd_ == CONTROL_CMD_PREPARE) {
1607         CreateClientThread();
1608         if (!appPackage_.empty() && restart_) {
1609             if (!IsAppRestarted()) {
1610                 ChildResponseToMain(false);
1611                 CloseClientThread();
1612                 return HiperfError::CHECK_RESTART_OPTION_FAIL;
1613             }
1614             HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] App restart success");
1615         }
1616     }
1617 
1618     if (!isRoot_) {
1619         offset_ = GetOffsetNum();
1620         SymbolsFile::offsetNum_ = offset_;
1621         HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] get offset success");
1622     }
1623 
1624     if (!CheckTargetPids()) {
1625         HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] CheckTargetPids failed");
1626         if (controlCmd_ == CONTROL_CMD_PREPARE) {
1627             ChildResponseToMain(false);
1628             CloseClientThread();
1629         }
1630         if (controlCmd_.empty()) {
1631             return HiperfError::NO_ERR;
1632         } else {
1633             return HiperfError::CHECK_OPTION_PID_FAIL;
1634         }
1635     }
1636     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] CheckTargetPids success");
1637     std::string err = OHOS::Developtools::HiPerf::HandleAppInfo(appPackage_, inputPidTidArgs_);
1638     if (!err.empty()) {
1639         ChildResponseToMain(err);
1640         ChildResponseToMain(false);
1641         CloseClientThread();
1642         return HiperfError::CHECK_DEBUG_APP_FAIL;
1643     }
1644     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] HandleAppInfo finish");
1645     // prepare PerfEvents
1646     if (!PrepareSysKernel()) {
1647         if (controlCmd_ == CONTROL_CMD_PREPARE) {
1648             ChildResponseToMain(false);
1649             CloseClientThread();
1650         }
1651         return HiperfError::PREPARE_SYS_KERNEL_FAIL;
1652     }
1653     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] PrepareSysKernel finish");
1654     if (!PreparePerfEvent()) {
1655         if (controlCmd_ == CONTROL_CMD_PREPARE) {
1656             ChildResponseToMain(false);
1657             CloseClientThread();
1658         }
1659         return HiperfError::PREPARE_PERF_EVENT_FAIL;
1660     }
1661     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] PreparePerfEvent finish");
1662     // prepar some attr before CreateInitRecordFile
1663     if (!perfEvents_.PrepareTracking()) {
1664         if (controlCmd_ == CONTROL_CMD_PREPARE) {
1665             ChildResponseToMain(false);
1666             CloseClientThread();
1667         }
1668         HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] Fail to prepare tracking");
1669         HLOGE("Fail to prepare tracking");
1670         return HiperfError::PREPARE_TACKING_FAIL;
1671     }
1672     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord perfEvents prepared");
1673 
1674     if (!backtrack_ && !CreateInitRecordFile(delayUnwind_ ? false : compressData_)) {
1675         if (controlCmd_ == CONTROL_CMD_PREPARE) {
1676             ChildResponseToMain(false);
1677             CloseClientThread();
1678         }
1679         HLOGE("Fail to create record file %s", outputFilename_.c_str());
1680         HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] Fail to create record file");
1681         return HiperfError::CREATE_OUTPUT_FILE_FAIL;
1682     }
1683     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] CreateInitRecordFile finished");
1684     if (!PrepareVirtualRuntime()) {
1685         if (controlCmd_ == CONTROL_CMD_PREPARE) {
1686             ChildResponseToMain(false);
1687             CloseClientThread();
1688         }
1689         HLOGE("Fail to prepare virtualRuntime");
1690         HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] Fail to prepare virtualRuntime");
1691         return HiperfError::PREPARE_VIRTUAL_RUNTIME_FAIL;
1692     }
1693 
1694     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord virtualRuntime prepared");
1695 
1696     if (controlCmd_ == CONTROL_CMD_PREPARE || isHiperfClient_) {
1697         HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] CreateReplyThread start");
1698         CreateReplyThread();
1699     }
1700 
1701     if (isHiperfClient_) {
1702         HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] isHiperfClient_ CreateClientThread start");
1703         CreateClientThread();
1704     }
1705     //write comm event
1706     WriteCommEventBeforeSampling();
1707     SetExcludeHiperf();
1708     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord StartTracking");
1709     // if mmap record size has been larger than limit, dont start sampling.
1710     if (!isDataSizeLimitStop_) {
1711         if (restart_ && controlCmd_ == CONTROL_CMD_PREPARE) {
1712             RETURN_IF(!perfEvents_.StartTracking(isFifoServer_), HiperfError::PREPARE_START_TRACKING_FAIL);
1713         } else {
1714             RETURN_IF(!perfEvents_.StartTracking((!isFifoServer_) && (clientPipeInput_ == -1)),
1715                       HiperfError::START_TRACKING_FAIL);
1716         }
1717     }
1718     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord perfEvents tracking finish");
1719 
1720     if (isSpe_) {
1721         HLOGD("stop write spe record");
1722         fileWriter_->SetWriteRecordStat(false);
1723     }
1724     startSaveFileTimes_ = steady_clock::now();
1725     if (!backtrack_) {
1726         if (!FinishWriteRecordFile()) {
1727             HLOGE("Fail to finish record file %s", outputFilename_.c_str());
1728             HIPERF_HILOGE(MODULE_DEFAULT, "Fail to finish record file");
1729             return HiperfError::FINISH_WRITE_RECORD_FILE_FAIL;
1730         } else if (!PostProcessRecordFile()) {
1731             HLOGE("Fail to post process record file");
1732             HIPERF_HILOGE(MODULE_DEFAULT, "Fail to post process record file");
1733             return HiperfError::POST_PROCESS_RECORD_FILE;
1734         }
1735         RecordCompleted();
1736     }
1737 
1738     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord final report");
1739     // finial report
1740     RecoverSavedCmdlinesSize();
1741     OnlineReportData();
1742     CloseReplyThread();
1743     CloseClientThread();
1744     RemoveVdsoTmpFile();
1745     AgeHiperflogFiles();
1746     HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord finish");
1747     return HiperfError::NO_ERR;
1748 }
1749 
CloseClientThread()1750 void SubCommandRecord::CloseClientThread()
1751 {
1752     if (clientCommandHandle_.joinable()) {
1753         clientRunning_.store(false);
1754         HLOGI("CloseClientThread");
1755         if (nullFd_ != -1) {
1756             close(nullFd_);
1757         }
1758         clientCommandHandle_.join();
1759         close(clientPipeInput_);
1760         close(clientPipeOutput_);
1761         if (isFifoServer_) {
1762             RemoveFifoFile();
1763         }
1764     }
1765 }
1766 
CloseReplyThread()1767 void SubCommandRecord::CloseReplyThread()
1768 {
1769     if (replyCommandHandle_.joinable()) {
1770         clientRunning_.store(false);
1771         HLOGI("CloseReplyThread");
1772         replyCommandHandle_.join();
1773     }
1774 }
1775 
RemoveVdsoTmpFile()1776 void SubCommandRecord::RemoveVdsoTmpFile()
1777 {
1778     std::vector<std::string> fileName = {"/data/log/hiperflog/[shmm]", "/data/log/hiperflog/[vdso]"};
1779     for (auto name : fileName) {
1780         if (access(name.c_str(), F_OK) == 0) {
1781             if (remove(name.c_str()) != 0) {
1782                 char errInfo[ERRINFOLEN] = { 0 };
1783                 strerror_r(errno, errInfo, ERRINFOLEN);
1784                 HLOGE("remove file %s failed,errno:%d,errinfo:%s", name.c_str(), errno, errInfo);
1785             }
1786         }
1787     }
1788 }
1789 
UpdateDevHostMapsAndIPs(PerfEventRecord & record)1790 void SubCommandRecord::UpdateDevHostMapsAndIPs(PerfEventRecord& record)
1791 {
1792     if (isRoot_) {
1793         return;
1794     }
1795     UpdateDevHostCallChains(record);
1796     UpdateDevHostMaps(record);
1797 }
1798 
ProcessRecord(PerfEventRecord & record)1799 bool SubCommandRecord::ProcessRecord(PerfEventRecord& record)
1800 {
1801     CHECK_TRUE(record.GetName() != nullptr, false, 1, "record is null");
1802 #if HIDEBUG_RECORD_NOT_PROCESS
1803     // some times we want to check performance
1804     // but we still want to see the record number
1805     if (record.GetType() == PERF_RECORD_SAMPLE) {
1806         recordSamples_++;
1807     } else {
1808         recordNoSamples_++;
1809     }
1810     return true;
1811 #else
1812 #ifdef HIPERF_DEBUG_TIME
1813     const auto startTime = steady_clock::now();
1814 #endif
1815     if (record.GetType() == PERF_RECORD_SAMPLE) {
1816         PerfRecordSample& recordSample = static_cast<PerfRecordSample&>(record);
1817         if (IsThreadExcluded(recordSample.data_.pid, recordSample.data_.tid)) {
1818             return true;
1819         }
1820     } else if (record.GetType() == PERF_RECORD_COMM) {
1821         PerfRecordComm& recordComm = static_cast<PerfRecordComm&>(record);
1822         for (const auto& threadName : excludeThreadNameArgs_) {
1823             if (threadName.compare(recordComm.data_.comm) == 0) {
1824                 excludeTids_.insert(recordComm.data_.tid);
1825                 break;
1826             }
1827         }
1828     }
1829 
1830     if (backtrack_ && !perfEvents_.IsOutputTracking()) {
1831         return true;
1832     }
1833 
1834     // May create some simulated events
1835     // it will call ProcessRecord before next line
1836 #if !HIDEBUG_RECORD_NOT_PROCESS_VM
1837     virtualRuntime_.UpdateFromRecord(record);
1838 #endif
1839 #ifdef HIPERF_DEBUG_TIME
1840     prcessRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
1841 #endif
1842     UpdateDevHostMapsAndIPs(record);
1843     return SaveRecord(record);
1844 #endif
1845 }
1846 
SaveRecord(const PerfEventRecord & record)1847 bool SubCommandRecord::SaveRecord(const PerfEventRecord& record)
1848 {
1849 #ifdef HIPERF_UNITTEST
1850     if (checkCallback_ != nullptr) {
1851         checkCallback_(record);
1852     }
1853 #endif
1854     if (fileWriter_ == nullptr) {
1855         return false;
1856     }
1857 #if HIDEBUG_RECORD_NOT_SAVE
1858     return true;
1859 #endif
1860     if (dataSizeLimit_ > 0u) {
1861         if (dataSizeLimit_ <= fileWriter_->GetDataSize()) {
1862             CHECK_TRUE(!isDataSizeLimitStop_, false, 0, "");
1863             printf("record size %" PRIu64 " is large than limit %" PRIu64 ". stop sampling.\n",
1864                 fileWriter_->GetDataSize(), dataSizeLimit_);
1865             perfEvents_.StopTracking();
1866             isDataSizeLimitStop_ = true;
1867             return false;
1868         }
1869     }
1870 
1871     if (record.GetName() != nullptr) {
1872 #ifdef HIPERF_DEBUG_TIME
1873         const auto saveTime = steady_clock::now();
1874 #endif
1875         if (!fileWriter_->WriteRecord(record)) {
1876             // write file failed, need stop record
1877             perfEvents_.StopTracking();
1878             HLOGV("fail to write record %s", record.GetName());
1879             return false;
1880         }
1881         if (record.GetType() == PERF_RECORD_SAMPLE) {
1882             recordSamples_++;
1883         } else {
1884             recordNoSamples_++;
1885         }
1886         HLOGV(" write done. size=%zu name=%s", record.GetSize(), record.GetName());
1887 #ifdef HIPERF_DEBUG_TIME
1888         saveRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - saveTime);
1889 #endif
1890         return true;
1891     }
1892     return false;
1893 }
1894 
GetCountFromFile(const std::string & fileName)1895 uint32_t SubCommandRecord::GetCountFromFile(const std::string &fileName)
1896 {
1897     uint32_t ret = 0;
1898     std::string str = ReadFileToString(fileName);
1899     std::vector<std::string> subStrs = StringSplit(str);
1900     for (auto subStr : subStrs) {
1901         ret++;
1902         std::vector<std::string> vSubstr = StringSplit(subStr, "-");
1903         static const size_t BEGIN_END = 2;
1904         if (vSubstr.size() != BEGIN_END) {
1905             continue;
1906         }
1907         int num1 = 0;
1908         int num2 = 0;
1909         if (vSubstr[1].size() >= 2 && StringEndsWith(vSubstr[1], "\n")) { // 2: string size
1910             vSubstr[1].resize(vSubstr[1].size() - 1);
1911         }
1912         bool ret1 = IsStringToIntSuccess(vSubstr[1], num1);
1913         bool ret2 = IsStringToIntSuccess(vSubstr[0], num2);
1914         if (ret1 && ret2) {
1915             ret += static_cast<uint32_t>(num1 - num2);
1916         }
1917     }
1918     return ret;
1919 }
1920 
GetCpuDescFromFile()1921 std::string SubCommandRecord::GetCpuDescFromFile()
1922 {
1923     std::string str = ReadFileToString("/proc/cpuinfo");
1924     std::vector<std::string> subStrs = StringSplit(str, "\n");
1925     for (auto subStr : subStrs) {
1926         if (subStr.find("model name") == std::string::npos) {
1927             continue;
1928         }
1929 
1930         std::vector<std::string> vSubstr = StringSplit(subStr, ": ");
1931         static const size_t NAME_VALUE = 2;
1932         if (vSubstr.size() == NAME_VALUE) {
1933             return vSubstr[1];
1934         } else {
1935             return "";
1936         }
1937     }
1938     return "";
1939 }
1940 
AddCpuFeature()1941 bool SubCommandRecord::AddCpuFeature()
1942 {
1943     utsname unameBuf;
1944     if ((uname(&unameBuf)) != 0) {
1945         perror("uname() failed");
1946         return false;
1947     }
1948 
1949     fileWriter_->AddStringFeature(FEATURE::OSRELEASE, unameBuf.release);
1950     fileWriter_->AddStringFeature(FEATURE::HOSTNAME, unameBuf.nodename);
1951     fileWriter_->AddStringFeature(FEATURE::ARCH, unameBuf.machine);
1952 
1953     try {
1954         uint32_t cpuPresent = GetCountFromFile("/sys/devices/system/cpu/present");
1955         uint32_t cpuOnline = GetCountFromFile("/sys/devices/system/cpu/online");
1956         HLOGD("cpuPresent: %d, cpuOnline: %d", static_cast<int>(cpuPresent), static_cast<int>(cpuOnline));
1957         fileWriter_->AddNrCpusFeature(FEATURE::NRCPUS, cpuPresent - cpuOnline, cpuOnline);
1958     } catch (...) {
1959         HLOGD("get NRCPUS failed");
1960         return false;
1961     }
1962     std::string cpuDesc = GetCpuDescFromFile();
1963     if (!fileWriter_->AddStringFeature(FEATURE::CPUDESC, cpuDesc)) {
1964         return false;
1965     }
1966 
1967     // CPUID(vendor,family,model,stepping in /proc/cpuinfo) isn't supported on Hi3516DV300
1968     // CPU_TOPOLOGY(sockets,dies,threads), isn't supported on Hi3516DV300
1969     // NUMA_TOPOLOGY
1970     // HEADER_PMU_MAPPINGS(/sys/bus/event_source/devices/cpu/type) isn't supported on Hi3516DV300
1971 
1972     return true;
1973 }
1974 
AddMemTotalFeature()1975 void SubCommandRecord::AddMemTotalFeature()
1976 {
1977     std::string str = ReadFileToString("/proc/meminfo");
1978     std::vector<std::string> subStrs = StringSplit(str, " ");
1979     for (auto it = subStrs.begin(); it != subStrs.end(); it++) {
1980         if (it->find("MemTotal:") == std::string::npos) {
1981             continue;
1982         }
1983 
1984         if ((it + 1) != subStrs.end()) {
1985             uint64_t memTotal = 0;
1986             if (!StringToUint64(*(it + 1), memTotal)) {
1987                 HIPERF_HILOGE(MODULE_DEFAULT, "get uint64_t failed, paramValue: %{public}s", (*(it + 1)).c_str());
1988                 continue;
1989             }
1990             fileWriter_->AddU64Feature(FEATURE::TOTAL_MEM, memTotal);
1991         }
1992         break;
1993     }
1994 }
1995 
AddEventDescFeature()1996 void SubCommandRecord::AddEventDescFeature()
1997 {
1998     fileWriter_->AddEventDescFeature(FEATURE::EVENT_DESC, perfEvents_.GetAttrWithId());
1999 }
2000 
AddRecordTimeFeature()2001 void SubCommandRecord::AddRecordTimeFeature()
2002 {
2003     // create time
2004     std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
2005     // clang-format off
2006     char buf[256] = { 0 };
2007     ctime_r(&time, buf);
2008     fileWriter_->AddStringFeature(FEATURE::HIPERF_RECORD_TIME,
2009                                   StringReplace(buf, "\n", ""));
2010     // clang-format on
2011     return;
2012 }
2013 
AddWorkloadCmdFeature()2014 void SubCommandRecord::AddWorkloadCmdFeature()
2015 {
2016     if (trackedCommand_.size() > 0) {
2017         fileWriter_->AddStringFeature(FEATURE::HIPERF_WORKLOAD_CMD, trackedCommand_.at(0));
2018     } else {
2019         HLOGD("no trackedCommand");
2020     }
2021 }
2022 
AddCommandLineFeature()2023 void SubCommandRecord::AddCommandLineFeature()
2024 {
2025     // cmdline may end with some no ascii code
2026     // so we cp it with c_str again
2027     std::string fullCommandline =
2028         ReadFileToString("/proc/self/cmdline").c_str() + Command::fullArgument;
2029     fileWriter_->AddStringFeature(FEATURE::CMDLINE, fullCommandline);
2030 }
2031 
AddCpuOffFeature()2032 void SubCommandRecord::AddCpuOffFeature()
2033 {
2034     if (offCPU_) {
2035         fileWriter_->AddBoolFeature(FEATURE::HIPERF_CPU_OFF);
2036     }
2037 }
2038 
AddDevhostFeature()2039 void SubCommandRecord::AddDevhostFeature()
2040 {
2041     if (isHM_) {
2042         fileWriter_->AddStringFeature(FEATURE::HIPERF_HM_DEVHOST,
2043             StringPrintf("%d", virtualRuntime_.devhostPid_));
2044     }
2045 }
2046 
AddFeatureRecordFile()2047 bool SubCommandRecord::AddFeatureRecordFile()
2048 {
2049     // VERSION
2050     CHECK_TRUE(AddCpuFeature(), false, 0, "");
2051     AddMemTotalFeature();
2052 
2053     AddCommandLineFeature();
2054 
2055     AddEventDescFeature();
2056 
2057     AddRecordTimeFeature();
2058 
2059     AddWorkloadCmdFeature();
2060 
2061     AddCpuOffFeature();
2062 
2063     AddDevhostFeature();
2064 
2065     return true;
2066 }
2067 
CreateInitRecordFile(const bool compressData)2068 bool SubCommandRecord::CreateInitRecordFile(const bool compressData)
2069 {
2070     if (fileWriter_ == nullptr) {
2071         fileWriter_ = std::make_unique<PerfFileWriter>();
2072     }
2073 
2074     if (!fileWriter_->Open(outputFilename_, compressData)) {
2075         return false;
2076     }
2077 
2078     CHECK_TRUE(fileWriter_->WriteAttrAndId(perfEvents_.GetAttrWithId(), isSpe_), false, 0, "");
2079 
2080     CHECK_TRUE(AddFeatureRecordFile(), false, 0, "");
2081 
2082     HLOGD("create new record file %s", outputFilename_.c_str());
2083     return true;
2084 }
2085 
PostProcessRecordFile()2086 bool SubCommandRecord::PostProcessRecordFile()
2087 {
2088     if (delayUnwind_) {
2089         // 1. prepare file to rewrite
2090         std::string tempFileName = outputFilename_ + ".tmp";
2091         if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) {
2092             HLOGE("rename failed. unabel to do delay unwind");
2093             perror("Fail to rename data file");
2094             return false;
2095         } else {
2096             HLOGD("use temp file '%s' for delay unwind", tempFileName.c_str());
2097         }
2098 
2099         // renew record file
2100         // release the old one
2101         fileWriter_.reset();
2102         if (!CreateInitRecordFile(compressData_)) {
2103             // create again
2104             HLOGEP("Fail to open data file %s ", outputFilename_.c_str());
2105             return false;
2106         }
2107 
2108         // read temp file
2109         auto fileReader = PerfFileReader::Instance(tempFileName);
2110         if (fileReader == nullptr) {
2111             HLOGEP("Fail to open data file %s ", tempFileName.c_str());
2112             return false;
2113         }
2114 
2115         // 2. read out the file and unwind
2116         auto record_callback = [&](PerfEventRecord& record) {
2117             if (record.GetName() == nullptr) {
2118                 // return false in callback can stop the read process
2119                 return false;
2120             } else if (record.GetType() == PERF_RECORD_SAMPLE) {
2121                 HLOGM("readback record for unwind");
2122                 virtualRuntime_.UnwindFromRecord(static_cast<PerfRecordSample&>(record));
2123             }
2124             SaveRecord(record);
2125             return true;
2126         };
2127         fileReader->ReadDataSection(record_callback);
2128 
2129         // 3. close again
2130 
2131         // lte FinishWriteRecordFile write matched only symbols
2132         delayUnwind_ = false;
2133         CHECK_TRUE(FinishWriteRecordFile(), false, 1, "Fail to finish record file %s", outputFilename_.c_str());
2134 
2135         remove(tempFileName.c_str());
2136     }
2137     return true;
2138 }
2139 
2140 #if USE_COLLECT_SYMBOLIC
SymbolicHits()2141 void SubCommandRecord::SymbolicHits()
2142 {
2143     if (isHM_) {
2144         for (auto &processPair : kernelThreadSymbolsHits_) {
2145             for (auto &vaddr : processPair.second) {
2146                 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first,
2147                                           PERF_CONTEXT_MAX);
2148             }
2149         }
2150     }
2151 
2152     for (auto &vaddr : kernelSymbolsHits_) {
2153         virtualRuntime_.GetSymbol(vaddr, 0, 0, PERF_CONTEXT_KERNEL);
2154     }
2155 
2156     for (auto &processPair : userSymbolsHits_) {
2157         for (auto &vaddr : processPair.second) {
2158             virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first,
2159                                       PERF_CONTEXT_USER);
2160         }
2161     }
2162 }
2163 #endif
2164 
CollectionSymbol(PerfEventRecord & record)2165 bool SubCommandRecord::CollectionSymbol(PerfEventRecord& record)
2166 {
2167     CHECK_TRUE(record.GetName() != nullptr, false, 0, "");
2168     if (record.GetType() == PERF_RECORD_SAMPLE) {
2169         PerfRecordSample* sample = static_cast<PerfRecordSample*>(&record);
2170 #if USE_COLLECT_SYMBOLIC
2171         CollectSymbol(sample);
2172 #else
2173         virtualRuntime_.SymbolicRecord(*sample);
2174 #endif
2175     }
2176 
2177     if (isSpe_ && record.GetType() == PERF_RECORD_AUXTRACE) {
2178         PerfRecordAuxtrace* sample = static_cast<PerfRecordAuxtrace*>(&record);
2179         virtualRuntime_.SymbolSpeRecord(*sample);
2180     }
2181 
2182     return true;
2183 }
2184 
CollectSymbol(PerfRecordSample * sample)2185 void SubCommandRecord::CollectSymbol(PerfRecordSample *sample)
2186 {
2187     CHECK_TRUE(sample != nullptr, NO_RETVAL, 0, "");
2188     perf_callchain_context context = sample->InKernel() ? PERF_CONTEXT_KERNEL
2189                                                         : PERF_CONTEXT_USER;
2190     pid_t serverPid;
2191     // if no nr use ip ? remove stack nr == 0?
2192     if (sample->data_.nr == 0) {
2193         serverPid = sample->GetServerPidof(0);
2194         if (virtualRuntime_.IsKernelThread(serverPid)) {
2195             kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ip);
2196         } else if (context == PERF_CONTEXT_KERNEL) {
2197             kernelSymbolsHits_.insert(sample->data_.ip);
2198         } else {
2199             userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip);
2200         }
2201         return;
2202     }
2203 
2204     for (u64 i = 0; i < sample->data_.nr; i++) {
2205         if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) {
2206             if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) {
2207                 context = PERF_CONTEXT_KERNEL;
2208             } else {
2209                 context = PERF_CONTEXT_USER;
2210             }
2211             continue;
2212         }
2213 
2214         serverPid = sample->GetServerPidof(i);
2215         if (!isRoot_ && static_cast<uint32_t>(serverPid) == devhostPid_) {
2216             // in func UpdateDevHostCallChains add offset_ to ips, need sub offset_ when symboling
2217             if (sample->data_.ips[i] > offset_) {
2218                 sample->data_.ips[i] -= offset_;
2219             }
2220         }
2221         if (virtualRuntime_.IsKernelThread(serverPid)) {
2222             kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ips[i]);
2223         } else if (context == PERF_CONTEXT_KERNEL) {
2224             kernelSymbolsHits_.insert(sample->data_.ips[i]);
2225         } else {
2226             userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]);
2227         }
2228     }
2229 }
2230 
2231 // finish writing data file, then close file
FinishWriteRecordFile()2232 bool SubCommandRecord::FinishWriteRecordFile()
2233 {
2234 #ifdef HIPERF_DEBUG_TIME
2235     const auto startTime = steady_clock::now();
2236 #endif
2237 #if !HIDEBUG_SKIP_PROCESS_SYMBOLS
2238     if (!delayUnwind_) {
2239 #if !HIDEBUG_SKIP_LOAD_KERNEL_SYMBOLS
2240         if (!callChainUserOnly_) {
2241             virtualRuntime_.UpdateKernelSymbols();
2242             virtualRuntime_.UpdateKernelModulesSymbols();
2243             if (isHM_) {
2244                 virtualRuntime_.UpdateServiceSymbols();
2245             }
2246         }
2247         if (isHM_) {
2248             virtualRuntime_.UpdateDevhostSymbols();
2249         }
2250 #endif
2251         HLOGD("Load user symbols");
2252         if (dedupStack_) {
2253             virtualRuntime_.CollectDedupSymbol(kernelSymbolsHits_, userSymbolsHits_);
2254         } else {
2255             fileWriter_->ReadDataSection(
2256                 [this] (PerfEventRecord& record) -> bool {
2257                     return this->CollectionSymbol(record);
2258                 });
2259         }
2260 #if USE_COLLECT_SYMBOLIC
2261         SymbolicHits();
2262 #endif
2263 #if HIDEBUG_SKIP_MATCH_SYMBOLS
2264         disableUnwind_ = true;
2265 #endif
2266 #if !HIDEBUG_SKIP_SAVE_SYMBOLS
2267         CHECK_TRUE(fileWriter_->AddSymbolsFeature(virtualRuntime_.GetSymbolsFiles()),
2268                    false, 1, "Fail to AddSymbolsFeature");
2269 #endif
2270     }
2271 #endif
2272     CHECK_TRUE(!dedupStack_ || fileWriter_->AddUniStackTableFeature(virtualRuntime_.GetUniStackTable()), false, 0, "");
2273 
2274     if (backtrack_) {
2275         virtualRuntime_.ClearSymbolCache();
2276 #if USE_COLLECT_SYMBOLIC
2277         kernelThreadSymbolsHits_.clear();
2278         kernelSymbolsHits_.clear();
2279         userSymbolsHits_.clear();
2280 #endif
2281     }
2282 
2283     CHECK_TRUE(fileWriter_->Close(), false, 1, "Fail to close record file %s", outputFilename_.c_str());
2284 #ifdef HIPERF_DEBUG_TIME
2285     saveFeatureTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
2286 #endif
2287     return true;
2288 }
2289 
2290 #ifdef HIPERF_DEBUG_TIME
ReportTime()2291 void SubCommandRecord::ReportTime()
2292 {
2293     printf("updateSymbolsTimes: %0.3f ms\n",
2294            virtualRuntime_.updateSymbolsTimes_.count() / MS_DURATION);
2295     printf("saveFeatureTimes: %0.3f ms\n", saveFeatureTimes_.count() / MS_DURATION);
2296 
2297     printf("prcessRecordTimes: %0.3f ms\n", prcessRecordTimes_.count() / MS_DURATION);
2298     printf("-prcessSampleRecordTimes: %0.3f ms\n",
2299            virtualRuntime_.processSampleRecordTimes_.count() / MS_DURATION);
2300     printf("--unwindFromRecordTimes: %0.3f ms\n",
2301            virtualRuntime_.unwindFromRecordTimes_.count() / MS_DURATION);
2302     printf("-prcessMmapRecordTimes: %0.3f ms\n",
2303            virtualRuntime_.processMmapRecordTimes_.count() / MS_DURATION);
2304     printf("-prcessMmap2RecordTimes: %0.3f ms\n",
2305            virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION);
2306     printf("-prcessCommRecordTimes: %0.3f ms\n",
2307            virtualRuntime_.processCommRecordTimes_.count() / MS_DURATION);
2308     printf("-prcessMmap2RecordTimes: %0.3f ms\n",
2309            virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION);
2310     printf("--updateThreadTimes: %0.3f ms\n",
2311            virtualRuntime_.updateThreadTimes_.count() / MS_DURATION);
2312     printf("---threadParseMapsTimes: %0.3f ms\n",
2313            virtualRuntime_.threadParseMapsTimes_.count() / MS_DURATION);
2314     printf("---threadCreateMmapTimes: %0.3f ms\n",
2315            virtualRuntime_.threadCreateMmapTimes_.count() / MS_DURATION);
2316     printf("--unwindCallStackTimes: %0.3f ms\n",
2317            virtualRuntime_.unwindCallStackTimes_.count() / MS_DURATION);
2318     printf("-symbolicRecordTimes: %0.3f ms\n",
2319            virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION);
2320     printf("saveRecordTimes: %0.3f ms\n", saveRecordTimes_.count() / MS_DURATION);
2321     printf("-writeTimes: %0.3f ms\n", fileWriter_->writeTimes_.count() / MS_DURATION);
2322 
2323     printf("logTimes: %0.3f ms\n", DebugLogger::GetInstance()->logTimes_.count() / MS_DURATION);
2324     printf("-logSprintfTimes: %0.3f ms\n",
2325            DebugLogger::GetInstance()->logSprintfTimes_.count() / MS_DURATION);
2326     printf("-logWriteTimes: %0.3f ms\n",
2327            DebugLogger::GetInstance()->logWriteTimes_.count() / MS_DURATION);
2328     printf("logCount: %zu (%4.2f ms/log)\n", DebugLogger::GetInstance()->logCount_,
2329            DebugLogger::GetInstance()->logTimes_.count() /
2330                static_cast<double>(DebugLogger::GetInstance()->logCount_) / MS_DURATION);
2331 }
2332 #endif
2333 
RecordCompleted()2334 bool SubCommandRecord::RecordCompleted()
2335 {
2336     if (verboseReport_) {
2337         printf("Save Record used %0.3f ms.\n",
2338                duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() /
2339                    MS_DURATION);
2340     }
2341     HLOGV("Save Record used %0.3f ms.\n",
2342           duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() /
2343               MS_DURATION);
2344 
2345     // print brief file info
2346     double mb = static_cast<double>(fileWriter_->GetDataSize()) / (KILO * KILO);
2347     if (compressData_) {
2348         printf("[ hiperf record: Captured and compressed %.3f MB perf data. ]\n", mb);
2349     } else {
2350         printf("[ hiperf record: Captured %.3f MB perf data. ]\n", mb);
2351     }
2352     printf("[ Sample records: %zu, Non sample records: %zu ]\n", recordSamples_, recordNoSamples_);
2353     // Show brief sample lost.
2354     size_t lostSamples = 0;
2355     size_t lostNonSamples = 0;
2356     perfEvents_.GetLostSamples(lostSamples, lostNonSamples);
2357     printf("[ Sample lost: %zu, Non sample lost: %zu ]\n", lostSamples, lostNonSamples);
2358 
2359 #ifdef HIPERF_DEBUG_TIME
2360     ReportTime();
2361 #endif
2362     return true;
2363 }
2364 
RegisterSubCommandRecord(void)2365 bool SubCommandRecord::RegisterSubCommandRecord(void)
2366 {
2367     return SubCommand::RegisterSubCommand("record", SubCommandRecord::GetInstance);
2368 }
2369 
SetHM()2370 void SubCommandRecord::SetHM()
2371 {
2372     isHM_ = IsHM();
2373     virtualRuntime_.SetHM(isHM_);
2374     perfEvents_.SetHM(isHM_);
2375     HLOGD("Set isHM_: %d", isHM_);
2376     if (isHM_) {
2377         // find devhost pid
2378         const std::string basePath {"/proc/"};
2379         std::vector<std::string> subDirs = GetSubDirs(basePath);
2380         for (const auto &subDir : subDirs) {
2381             if (!IsDigits(subDir)) {
2382                 continue;
2383             }
2384             pid_t pid = std::stoll(subDir);
2385             std::string cmdline = GetProcessName(pid);
2386             if (cmdline == "/bin/" + DEVHOST_FILE_NAME) {
2387                 virtualRuntime_.SetDevhostPid(pid);
2388                 devhostPid_ = static_cast<uint32_t>(pid);
2389                 break;
2390             }
2391         }
2392     }
2393 }
2394 
OnlineReportData()2395 bool SubCommandRecord::OnlineReportData()
2396 {
2397     if (!report_) {
2398         return true;
2399     }
2400     HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s begin to report file",
2401                   __FUNCTION__);
2402     bool ret = false;
2403     std::string tempFileName = outputFilename_ + ".tmp";
2404     if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) {
2405         char errInfo[ERRINFOLEN] = { 0 };
2406         strerror_r(errno, errInfo, ERRINFOLEN);
2407         HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s can't rename file "
2408                       "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n",
2409                       __FUNCTION__, errno, errInfo);
2410         return false;
2411     }
2412 
2413     std::unique_ptr<SubCommandReport> reporter = std::make_unique<SubCommandReport>();
2414     HLOGD("report the file %s to report file %s \n", tempFileName.c_str(), outputFilename_.c_str());
2415     std::vector<std::string> args;
2416     args.emplace_back("-i");
2417     args.emplace_back(tempFileName);
2418     args.emplace_back("-o");
2419     args.emplace_back(outputFilename_);
2420     args.emplace_back("-s");
2421     if (reporter->ParseOption(args)) {
2422         ret =  (reporter->OnSubCommand(args) != HiperfError::NO_ERR);
2423     }
2424 
2425     if (remove(tempFileName.c_str()) != 0) {
2426         char errInfo[ERRINFOLEN] = { 0 };
2427         strerror_r(errno, errInfo, ERRINFOLEN);
2428         HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s remove file failed"
2429                       "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n",
2430                       __FUNCTION__, errno, errInfo);
2431     }
2432     HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s report result %" HILOG_PUBLIC "s",
2433                   __FUNCTION__, ret ? "success" : "fail");
2434     return ret;
2435 }
2436 
AddReportArgs(CommandReporter & reporter)2437 void SubCommandRecord::AddReportArgs(CommandReporter& reporter)
2438 {
2439     if (targetSystemWide_) {
2440         reporter.targetProcess_ = "ALL";
2441     } else if (!appPackage_.empty()) {
2442         reporter.targetProcess_ = appPackage_;
2443     } else {
2444         std::unordered_set<std::string> processNames = {};
2445         for_each(selectPids_.begin(), selectPids_.end(), [&processNames] (const pid_t& pid) {
2446             processNames.insert(GetProcessName(pid));
2447         });
2448         reporter.targetProcess_ = SetToString<std::string>(processNames);
2449     }
2450 }
2451 
CollectExcludeThread()2452 void SubCommandRecord::CollectExcludeThread()
2453 {
2454     if (!excludeProcessNameArgs_.empty()) {
2455         CollectPidsByAppname(excludePids_, excludeProcessNameArgs_);
2456     }
2457     excludeTids_.insert(excludeTidArgs_.begin(), excludeTidArgs_.end());
2458 }
2459 
SetExcludeHiperf()2460 void SubCommandRecord::SetExcludeHiperf()
2461 {
2462     if (excludeHiperf_) {
2463         excludePids_.emplace(getpid());
2464     }
2465 }
2466 
IsThreadExcluded(const pid_t pid,const pid_t tid)2467 bool SubCommandRecord::IsThreadExcluded(const pid_t pid, const pid_t tid)
2468 {
2469     if (excludePids_.find(pid) != excludePids_.end()) {
2470         return true;
2471     }
2472     if (excludeTids_.find(tid) != excludeTids_.end()) {
2473         return true;
2474     }
2475     return false;
2476 }
2477 
GetInstance()2478 SubCommand& SubCommandRecord::GetInstance()
2479 {
2480     static SubCommandRecord subCommand;
2481     return subCommand;
2482 }
2483 
SetCheckRecordCallback(CheckRecordCallBack callback)2484 void SubCommandRecord::SetCheckRecordCallback(CheckRecordCallBack callback)
2485 {
2486 #ifdef HIPERF_UNITTEST
2487     checkCallback_ = callback;
2488 #endif
2489 }
2490 
GetOffsetNum()2491 uint32_t SubCommandRecord::GetOffsetNum()
2492 {
2493     uint32_t result = 0;
2494     std::random_device rd;
2495     std::mt19937 gen(rd());
2496     std::uniform_int_distribution<uint32_t> dis(0, UINT32_MAX);
2497     result = dis(gen);
2498 
2499     std::ifstream randomFile("/dev/random", std::ios::binary);
2500     do {
2501         if (!randomFile.is_open()) {
2502             break;
2503         }
2504         randomFile.read(reinterpret_cast<char*>(&result), sizeof(result));
2505     } while (0);
2506     randomFile.close();
2507     return result;
2508 }
2509 
UpdateDevHostMaps(PerfEventRecord & record)2510 void SubCommandRecord::UpdateDevHostMaps(PerfEventRecord& record)
2511 {
2512     if (record.GetType() == PERF_RECORD_MMAP) {
2513         auto recordMmap = static_cast<PerfRecordMmap*>(&record);
2514         if (recordMmap->data_.pid == devhostPid_) {
2515             recordMmap->data_.addr += offset_;
2516         }
2517     } else if (record.GetType() == PERF_RECORD_MMAP2) {
2518         auto recordMmap2 = static_cast<PerfRecordMmap2*>(&record);
2519         if (recordMmap2->data_.pid == devhostPid_) {
2520             recordMmap2->data_.addr += offset_;
2521         }
2522     }
2523 }
2524 
UpdateDevHostCallChains(PerfEventRecord & record)2525 void SubCommandRecord::UpdateDevHostCallChains(PerfEventRecord& record)
2526 {
2527     if (record.GetType() == PERF_RECORD_SAMPLE) {
2528         uint32_t serverPid;
2529         const uint64_t BAD_IP_ADDRESS = 2;
2530         auto sample = static_cast<PerfRecordSample*>(&record);
2531         serverPid = static_cast<uint32_t>(sample->GetServerPidof(0));
2532         if (serverPid == devhostPid_) {
2533             sample->data_.ip += offset_;
2534         }
2535         for (u64 i = 0; i < sample->data_.nr; i++) {
2536             if (sample->data_.ips[i] >= PERF_CONTEXT_MAX || sample->data_.ips[i] < BAD_IP_ADDRESS) {
2537                 continue;
2538             }
2539             serverPid = static_cast<uint32_t>(sample->GetServerPidof(i));
2540             if (serverPid == devhostPid_) {
2541                 sample->data_.ips[i] += offset_;
2542             }
2543         }
2544     }
2545 }
2546 } // namespace HiPerf
2547 } // namespace Developtools
2548 } // namespace OHOS
2549