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