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