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