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