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