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