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