• 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