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