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