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