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 <random>
25
26 #if defined(CONFIG_HAS_SYSPARA)
27 #include <parameters.h>
28 #endif
29 #include <sys/stat.h>
30 #include <sys/utsname.h>
31 #include <unistd.h>
32
33 #include "command.h"
34 #include "debug_logger.h"
35 #include "hiperf_client.h"
36 #include "ipc_utilities.h"
37 #if defined(is_ohos) && is_ohos
38 #include "hiperf_hilog.h"
39 #endif
40 #include "option.h"
41 #include "perf_event_record.h"
42 #include "perf_file_reader.h"
43 #include "utilities.h"
44 #include "subcommand_report.h"
45
46 using namespace std::chrono;
47 namespace OHOS {
48 namespace Developtools {
49 namespace HiPerf {
50 const std::string PERF_CPU_TIME_MAX_PERCENT = "/proc/sys/kernel/perf_cpu_time_max_percent";
51 const std::string PERF_EVENT_MAX_SAMPLE_RATE = "/proc/sys/kernel/perf_event_max_sample_rate";
52 const std::string PERF_EVENT_MLOCK_KB = "/proc/sys/kernel/perf_event_mlock_kb";
53 const std::string SCHED_SWITCH = "/sys/kernel/tracing/events/sched/sched_switch/enable";
54 const std::string SCHED_SWITCH_DEBUG = "/sys/kernel/debug/tracing/events/sched/sched_switch/enable";
55 const std::string PROC_VERSION = "/proc/version";
56 const std::string SAVED_CMDLINES_SIZE = "/sys/kernel/tracing/saved_cmdlines_size";
57
58 // when there are many events, start record will take more time.
59 const std::chrono::milliseconds CONTROL_WAITREPY_TIMEOUT = 2000ms;
60 const int CONTROL_WAIT_RESPONSE_TIMEOUT = 35000; // 35s: Timeout for main process wait app restart finish
61 constexpr const uint8_t WAIT_TIMEOUT = 40; // 40s: Timeout for the main process exit
62
63 constexpr uint64_t MASK_ALIGNED_8 = 7;
64 constexpr size_t MAX_DWARF_CALL_CHAIN = 2;
65 constexpr uint64_t TYPE_PERF_SAMPLE_BRANCH = PERF_SAMPLE_BRANCH_ANY | PERF_SAMPLE_BRANCH_ANY_CALL |
66 PERF_SAMPLE_BRANCH_ANY_RETURN | PERF_SAMPLE_BRANCH_IND_JUMP |
67 PERF_SAMPLE_BRANCH_IND_CALL | PERF_SAMPLE_BRANCH_COND |
68 PERF_SAMPLE_BRANCH_CALL;
69 static constexpr uint64_t PIPE_READ = 0;
70 static constexpr uint64_t PIPE_WRITE = 1;
71 static constexpr uint64_t CHECK_WAIT_TIME_MS = 200;
72 static constexpr uint32_t MAX_SERVER_OUTPUT_WAIT_COUNT = 600;
73 static std::atomic_bool g_callStop(false);
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 }
109 return it->second;
110 }
111
~SubCommandRecord()112 SubCommandRecord::~SubCommandRecord()
113 {
114 CloseReplyThread();
115 CloseClientThread();
116 if (readFd_ != -1) {
117 close(readFd_);
118 readFd_ = -1;
119 }
120 if (writeFd_ != -1) {
121 close(writeFd_);
122 writeFd_ = -1;
123 }
124 }
125
DumpOptions() const126 void SubCommandRecord::DumpOptions() const
127 {
128 printf("DumpOptions:\n");
129 printf(" targetSystemWide:\t%s\n", targetSystemWide_ ? "true" : "false");
130 printf(" selectCpus:\t%s\n", VectorToString(selectCpus_).c_str());
131 printf(" timeStopSec:\t%f sec\n", timeStopSec_);
132 printf(" frequency:\t%d\n", frequency_);
133 printf(" selectEvents:\t%s\n", VectorToString(selectEvents_).c_str());
134 int i = 0;
135 for (auto &group : selectGroups_) {
136 i++;
137 printf(" selectGroups:\t%2d:%s\n", i, VectorToString(group).c_str());
138 }
139 printf(" no_inherit:\t%s\n", noInherit_ ? "true" : "false");
140 printf(" selectPids:\t%s\n", VectorToString(selectPids_).c_str());
141 printf(" selectTids:\t%s\n", VectorToString(selectTids_).c_str());
142 printf(" excludeTids:\t%s\n", VectorToString(excludeTidArgs_).c_str());
143 printf(" excludeThreads:\t%s\n", VectorToString(excludeThreadNameArgs_).c_str());
144 printf(" excludeProcessName_:\t%s\n", VectorToString(excludeProcessNameArgs_).c_str());
145 printf(" kernelCallChain:\t%s\n", kernelCallChain_ ? "true" : "false");
146 printf(" callChainUserOnly_:\t%s\n", callChainUserOnly_ ? "true" : "false");
147 printf(" restart:\t%s\n", restart_ ? "true" : "false");
148 printf(" verbose:\t%s\n", verboseReport_ ? "true" : "false");
149 printf(" excludePerf:\t%d\n", excludeHiperf_);
150 printf(" cpuPercent:\t%d\n", cpuPercent_);
151 printf(" offCPU_:\t%d\n", offCPU_);
152 printf(" delayUnwind_:\t%d\n", delayUnwind_);
153 printf(" disableUnwind_:\t%d\n", disableUnwind_);
154 printf(" disableCallstackExpend_:\t%d\n", disableCallstackExpend_);
155 printf(" enableDebugInfoSymbolic:\t%d\n", enableDebugInfoSymbolic_);
156 printf(" symbolDir_:\t%s\n", VectorToString(symbolDir_).c_str());
157 printf(" outputFilename_:\t%s\n", outputFilename_.c_str());
158 printf(" appPackage_:\t%s\n", appPackage_.c_str());
159 printf(" checkAppMs_:\t%d\n", checkAppMs_);
160 printf(" clockId_:\t%s\n", clockId_.c_str());
161 printf(" mmapPages_:\t%d\n", mmapPages_);
162 printf(" dataLimit:\t%s\n", strLimit_.c_str());
163 printf(" callStack:\t%s\n", VectorToString(callStackType_).c_str());
164 printf(" branchSampleTypes:\t%s\n", VectorToString(vecBranchFilters_).c_str());
165 printf(" trackedCommand:\t%s\n", VectorToString(trackedCommand_).c_str());
166 printf(" pipe_input:\t%d\n", clientPipeInput_);
167 printf(" pipe_output:\t%d\n", clientPipeOutput_);
168 printf(" cmdlinesSize_:\t%d\n", cmdlinesSize_);
169 printf(" report_:\t%s\n", report_ ? "true" : "false");
170 printf(" backtrack_:\t%s\n", backtrack_ ? "true" : "false");
171 printf(" backtrackTime_:\t%" PRIu64 "\n", backtrackTime_);
172 }
173
GetSpeOptions()174 bool SubCommandRecord::GetSpeOptions()
175 {
176 std::string speName;
177
178 for (size_t i = 0; i < selectEvents_.size(); i++) {
179 std::string optionValue = selectEvents_[i];
180
181 std::vector<std::string> valueExpressions = StringSplit(optionValue, "/");
182 if (i == 0) {
183 if (valueExpressions.size() > 1) {
184 speName = valueExpressions[0];
185 valueExpressions.erase(valueExpressions.begin());
186 } else {
187 break;
188 }
189 }
190
191 for (auto item: valueExpressions) {
192 std::vector<std::string> expressions = StringSplit(item, "=");
193 size_t itemNum = 2;
194 if (expressions.size() == itemNum && (IsNumeric(expressions[1]) || IsHexDigits(expressions[1]))) {
195 std::string name = expressions[0];
196 uint64_t num = 0;
197 char *endPtr = nullptr;
198 errno = 0;
199 if (IsNumeric(expressions[1])) {
200 num = std::strtoull(expressions[1].c_str(), &endPtr, 10); // 10 : decimal scale
201 } else {
202 num = std::strtoull(expressions[1].c_str(), &endPtr, NUMBER_FORMAT_HEX_BASE);
203 }
204 if (endPtr == expressions[1].c_str() || *endPtr != '\0' || errno != 0) {
205 HLOGE("string to uint64_t failed, expressions[1]: %s", expressions[1].c_str());
206 continue;
207 }
208 if (speOptMap_.find(name) != speOptMap_.end()) {
209 speOptMap_[name] = num;
210 }
211 }
212 }
213 }
214 if (speName.size() > 0) {
215 selectEvents_.clear();
216 selectEvents_.emplace_back(speName);
217 }
218 return true;
219 }
220
GetOptions(std::vector<std::string> & args)221 bool SubCommandRecord::GetOptions(std::vector<std::string> &args)
222 {
223 if (!Option::GetOptionValue(args, "-a", targetSystemWide_)) {
224 return false;
225 }
226 if (targetSystemWide_ && !IsSupportNonDebuggableApp()) {
227 HLOGD("-a option needs root privilege for system wide profiling.");
228 printf("-a option needs root privilege for system wide profiling.\n");
229 return false;
230 }
231 if (!Option::GetOptionValue(args, "--exclude-hiperf", excludeHiperf_)) {
232 return false;
233 }
234 if (!Option::GetOptionValue(args, "--control", controlCmd_)) {
235 return false;
236 }
237 allowIpc_ = controlCmd_ != CONTROL_CMD_PREPARE;
238 if (!Option::GetOptionValue(args, "-z", compressData_)) {
239 return false;
240 }
241 if (!Option::GetOptionValue(args, "--no-inherit", noInherit_)) {
242 return false;
243 }
244 if (!Option::GetOptionValue(args, "--offcpu", offCPU_)) {
245 return false;
246 }
247 if (!Option::GetOptionValue(args, "--delay-unwind", delayUnwind_)) {
248 return false;
249 }
250 if (!Option::GetOptionValue(args, "--disable-unwind", disableUnwind_)) {
251 return false;
252 }
253 if (!Option::GetOptionValue(args, "--disable-callstack-expand", disableCallstackExpend_)) {
254 return false;
255 }
256 if (!Option::GetOptionValue(args, "--enable-debuginfo-symbolic", enableDebugInfoSymbolic_)) {
257 return false;
258 }
259 if (!Option::GetOptionValue(args, "--verbose", verboseReport_)) {
260 return false;
261 }
262 if (!Option::GetOptionValue(args, "-d", timeStopSec_)) {
263 return false;
264 }
265 if (!GetOptionFrequencyAndPeriod(args)) {
266 return false;
267 }
268 if (!Option::GetOptionValue(args, "--cpu-limit", cpuPercent_)) {
269 return false;
270 }
271 if (!Option::GetOptionValue(args, "-m", mmapPages_)) {
272 return false;
273 }
274 if (!Option::GetOptionValue(args, "--symbol-dir", symbolDir_)) {
275 return false;
276 }
277 if (!Option::GetOptionValue(args, "-o", outputFilename_)) {
278 return false;
279 }
280 if (!Option::GetOptionValue(args, "--app", appPackage_)) {
281 return false;
282 }
283 std::string err = "";
284 if (allowIpc_ && !IsExistDebugByApp(appPackage_, err)) {
285 return false;
286 }
287 if (!Option::GetOptionValue(args, "--chkms", checkAppMs_)) {
288 return false;
289 }
290 if (!Option::GetOptionValue(args, "--clockid", clockId_)) {
291 return false;
292 }
293 if (!Option::GetOptionValue(args, "-c", selectCpus_)) {
294 return false;
295 }
296 if (!Option::GetOptionValue(args, "-p", selectPids_)) {
297 return false;
298 }
299 inputPidTidArgs_ = selectPids_;
300 if (!Option::GetOptionValue(args, "-t", selectTids_)) {
301 return false;
302 }
303 inputPidTidArgs_.insert(inputPidTidArgs_.end(), selectTids_.begin(), selectTids_.end());
304 if (!Option::GetOptionValue(args, "-e", selectEvents_)) {
305 return false;
306 }
307 if (!Option::GetOptionValue(args, "-g", selectGroups_)) {
308 return false;
309 }
310 if (!GetSpeOptions()) {
311 return false;
312 }
313 if (!Option::GetOptionValue(args, "-s", callStackType_)) {
314 return false;
315 }
316 if (!Option::GetOptionValue(args, "--kernel-callchain", kernelCallChain_)) {
317 return false;
318 }
319 if (!Option::GetOptionValue(args, "--callchain-useronly", callChainUserOnly_)) {
320 return false;
321 }
322 if (!Option::GetOptionValue(args, "--exclude-tid", excludeTidArgs_)) {
323 return false;
324 }
325 if (!Option::GetOptionValue(args, "--exclude-thread", excludeThreadNameArgs_)) {
326 return false;
327 }
328 if (!Option::GetOptionValue(args, "--restart", restart_)) {
329 return false;
330 }
331 std::vector<std::string> callStackType = {};
332 if (!Option::GetOptionValue(args, "--call-stack", callStackType)) {
333 return false;
334 }
335 if (!callStackType_.empty()) {
336 if (!callStackType.empty()) {
337 printf("'-s %s --call-stack %s' option usage error, please check usage.\n",
338 VectorToString(callStackType_).c_str(), VectorToString(callStackType).c_str());
339 return false;
340 }
341 } else {
342 callStackType_ = callStackType;
343 }
344 if (!Option::GetOptionValue(args, "--data-limit", strLimit_)) {
345 return false;
346 }
347 if (!Option::GetOptionValue(args, "-j", vecBranchFilters_)) {
348 return false;
349 }
350 if (!Option::GetOptionValue(args, "--pipe_input", clientPipeInput_)) {
351 return false;
352 }
353 if (!Option::GetOptionValue(args, "--pipe_output", clientPipeOutput_)) {
354 return false;
355 }
356 if (clientPipeInput_ != -1 && clientPipeOutput_ != -1) {
357 isHiperfClient_ = true;
358 }
359 if (!Option::GetOptionValue(args, "--dedup_stack", dedupStack_)) {
360 return false;
361 }
362 if (!Option::GetOptionValue(args, "--cmdline-size", cmdlinesSize_)) {
363 return false;
364 }
365 if (!Option::GetOptionValue(args, "--report", report_)) {
366 return false;
367 }
368 if (!Option::GetOptionValue(args, "--backtrack", backtrack_)) {
369 return false;
370 }
371 if (!Option::GetOptionValue(args, "--backtrack-sec", backtrackTime_)) {
372 return false;
373 }
374 if (!Option::GetOptionValue(args, "--exclude-process", excludeProcessNameArgs_)) {
375 return false;
376 }
377 if (targetSystemWide_ && dedupStack_) {
378 printf("-a option is conflict with --dedup_stack.\n");
379 return false;
380 }
381 CHECK_TRUE(Option::GetOptionTrackedCommand(args, trackedCommand_), false, 0, "");
382 CHECK_TRUE(args.empty(), false, LOG_TYPE_PRINTF,
383 "'%s' option usage error, please check usage.\n", VectorToString(args).c_str());
384 return true;
385 }
386
GetOptionFrequencyAndPeriod(std::vector<std::string> & args)387 bool SubCommandRecord::GetOptionFrequencyAndPeriod(std::vector<std::string> &args)
388 {
389 if (Option::FindOption(args, "-f") != args.end()) {
390 if (!Option::GetOptionValue(args, "-f", frequency_)) {
391 return false;
392 }
393 if (frequency_ < MIN_SAMPLE_FREQUENCY || frequency_ > MAX_SAMPLE_FREQUENCY) {
394 printf("Invalid -f value '%d', frequency should be in %d~%d \n", frequency_,
395 MIN_SAMPLE_FREQUENCY, MAX_SAMPLE_FREQUENCY);
396 return false;
397 }
398 }
399 if (Option::FindOption(args, "--period") != args.end()) {
400 if (frequency_ != 0) {
401 printf("option -f and --period is conflict, please check usage\n");
402 return false;
403 }
404 if (!Option::GetOptionValue(args, "--period", period_)) {
405 return false;
406 }
407 if (period_ <= 0) {
408 printf("Invalid --period value '%d', period should be greater than 0\n", period_);
409 return false;
410 }
411 }
412 return true;
413 }
414
CheckDataLimitOption()415 bool SubCommandRecord::CheckDataLimitOption()
416 {
417 if (!strLimit_.empty()) {
418 if (!ParseDataLimitOption(strLimit_)) {
419 printf("Invalid --data-limit value %s\n", strLimit_.c_str());
420 return false;
421 }
422 }
423 return true;
424 }
425
CheckSelectCpuPidOption()426 bool SubCommandRecord::CheckSelectCpuPidOption()
427 {
428 if (!selectCpus_.empty()) {
429 int cpuCount = sysconf(_SC_NPROCESSORS_CONF);
430 if (cpuCount == -1) {
431 printf("sysconf failed.\n");
432 return false;
433 }
434 int maxCpuid = cpuCount - 1;
435 for (auto cpu : selectCpus_) {
436 if (cpu < 0 || cpu > maxCpuid) {
437 printf("Invalid -c value '%d', the CPU ID should be in 0~%d \n", cpu, maxCpuid);
438 return false;
439 }
440 }
441 }
442
443 if (!selectPids_.empty()) {
444 for (auto pid : selectPids_) {
445 if (pid <= 0) {
446 printf("Invalid -p value '%d', the pid should be larger than 0\n", pid);
447 return false;
448 }
449 }
450 }
451 if (!selectTids_.empty()) {
452 for (auto tid : selectTids_) {
453 if (tid <= 0) {
454 printf("Invalid -t value '%d', the tid should be larger than 0\n", tid);
455 return false;
456 }
457 }
458 }
459 return true;
460 }
461
CheckArgsRange()462 bool SubCommandRecord::CheckArgsRange()
463 {
464 if (CheckOutOfRange<float>(timeStopSec_, MIN_STOP_SECONDS, MAX_STOP_SECONDS)) {
465 printf("Invalid -d value '%.3f', the seconds should be in %.3f~%.3f \n", timeStopSec_,
466 MIN_STOP_SECONDS, MAX_STOP_SECONDS);
467 return false;
468 }
469 if (CheckOutOfRange<int>(cpuPercent_, MIN_CPU_PERCENT, MAX_CPU_PERCENT)) {
470 printf("Invalid --cpu-limit value '%d', CPU percent should be in %d~%d \n", cpuPercent_,
471 MIN_CPU_PERCENT, MAX_CPU_PERCENT);
472 return false;
473 }
474 if (CheckOutOfRange<int>(checkAppMs_, MIN_CHECK_APP_MS, MAX_CHECK_APP_MS)) {
475 printf("Invalid --chkms value '%d', the milliseconds should be in %d~%d \n", checkAppMs_,
476 MIN_CHECK_APP_MS, MAX_CHECK_APP_MS);
477 return false;
478 }
479 if (CheckOutOfRange<uint64_t>(backtrackTime_, MIN_BACKTRACK_TIME_SEC, MAX_BACKTRACK_TIME_SEC)) {
480 printf("Invalid --backtrack-sec value '%" PRIu64 " ', value should be in %" PRIu64 "~%" PRIu64 " \n",
481 backtrackTime_, MIN_BACKTRACK_TIME_SEC, MAX_BACKTRACK_TIME_SEC);
482 return false;
483 }
484 if (CheckOutOfRange<int>(mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE) ||
485 !PowerOfTwo(mmapPages_)) {
486 printf("Invalid -m value '%d', value should be in %d~%d and must be a power of two \n",
487 mmapPages_, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE);
488 return false;
489 }
490 if (CheckOutOfRange<int>(cmdlinesSize_, MIN_SAVED_CMDLINES_SIZE, MAX_SAVED_CMDLINES_SIZE) ||
491 !PowerOfTwo(cmdlinesSize_)) {
492 printf("Invalid --cmdline-size value '%d', value should be in %d~%d and must be a power of two \n",
493 cmdlinesSize_, MIN_SAVED_CMDLINES_SIZE, MAX_SAVED_CMDLINES_SIZE);
494 return false;
495 }
496 if (!clockId_.empty() && GetClockId(clockId_) == -1) {
497 printf("Invalid --clockid value %s\n", clockId_.c_str());
498 return false;
499 }
500 if (!IsValidOutPath(outputFilename_)) {
501 printf("Invalid output file path, permission denied\n");
502 return false;
503 }
504 return true;
505 }
506
CheckExcludeArgs()507 bool SubCommandRecord::CheckExcludeArgs()
508 {
509 if (targetSystemWide_ && !excludeTidArgs_.empty()) {
510 printf("-a option is conflict with --exclude-tid.\n");
511 return false;
512 }
513 if (targetSystemWide_ && !excludeThreadNameArgs_.empty()) {
514 printf("-a option is conflict with --exclude-thread.\n");
515 return false;
516 }
517 if (!targetSystemWide_ && !excludeProcessNameArgs_.empty()) {
518 printf("--exclude-process must be used with -a.\n");
519 return false;
520 }
521 if (!targetSystemWide_ && excludeHiperf_) {
522 printf("--exclude-hiperf must be used with -a\n");
523 return false;
524 }
525 return true;
526 }
527
CheckOptions()528 bool SubCommandRecord::CheckOptions()
529 {
530 if (!CheckArgsRange()) {
531 return false;
532 }
533 if (!CheckExcludeArgs()) {
534 return false;
535 }
536 if (!CheckDataLimitOption()) {
537 return false;
538 }
539 if (!ParseCallStackOption(callStackType_)) {
540 return false;
541 }
542 if (!ParseBranchSampleType(vecBranchFilters_)) {
543 return false;
544 }
545 if (!CheckSelectCpuPidOption()) {
546 return false;
547 }
548 if (!ParseControlCmd(controlCmd_)) {
549 return false;
550 }
551 if (!CheckTargetProcessOptions()) {
552 return false;
553 }
554 std::string err = "";
555 if (allowIpc_ && !IsExistDebugByPid(inputPidTidArgs_, err)) {
556 return false;
557 }
558 if (!CheckReportOption()) {
559 return false;
560 }
561 if (!CheckBacktrackOption()) {
562 return false;
563 }
564 if (!CheckSpeOption()) {
565 return false;
566 }
567 return true;
568 }
569
570 #ifdef CONFIG_HAS_CCM
GetMmapPagesCfg()571 void SubCommandRecord::GetMmapPagesCfg()
572 {
573 size_t tmpPages = 0;
574 if (GetCfgValue(PRODUCT_CONFIG_PATH, CFG_MAP_PAGES, tmpPages)) {
575 int tmpValue = static_cast<int>(tmpPages);
576 if (CheckOutOfRange<int>(tmpValue, MIN_PERF_MMAP_PAGE, MAX_PERF_MMAP_PAGE) || !PowerOfTwo(tmpValue)) {
577 HIPERF_HILOGE(MODULE_DEFAULT, "GetCfgValue %{public}s faile, %{public}d out of range",
578 CFG_MAP_PAGES, tmpValue);
579 } else {
580 mmapPages_ = tmpValue;
581 HIPERF_HILOGI(MODULE_DEFAULT, "GetCfgValue %{public}s : %{public}d", CFG_MAP_PAGES, mmapPages_);
582 }
583 }
584 }
585 #endif
586
ParseOption(std::vector<std::string> & args)587 bool SubCommandRecord::ParseOption(std::vector<std::string> &args)
588 {
589 #ifdef CONFIG_HAS_CCM
590 GetMmapPagesCfg();
591 #endif
592 if (!GetOptions(args)) {
593 return false;
594 }
595 CHECK_TRUE(args.empty(), false, LOG_TYPE_PRINTF, "unknown option %s\n", args.begin()->c_str());
596 if (controlCmd_.empty()) {
597 if (!CheckRestartOption(appPackage_, targetSystemWide_, restart_, selectPids_)) {
598 return false;
599 }
600 }
601 return CheckOptions();
602 }
603
CheckTargetProcessOptions()604 bool SubCommandRecord::CheckTargetProcessOptions()
605 {
606 bool hasTarget = false;
607 if (targetSystemWide_) {
608 hasTarget = true;
609 }
610 if (!selectPids_.empty() || !selectTids_.empty()) {
611 CHECK_TRUE(!hasTarget, false, LOG_TYPE_PRINTF,
612 "-p/-t %s options conflict, please check usage\n", VectorToString(selectPids_).c_str());
613 hasTarget = true;
614 }
615 if (!trackedCommand_.empty()) {
616 if (hasTarget) {
617 printf("%s options conflict, please check usage\n",
618 VectorToString(trackedCommand_).c_str());
619 return false;
620 }
621 if (!IsRoot()) {
622 printf("%s options needs root privilege, please check usage\n",
623 VectorToString(trackedCommand_).c_str());
624 return false;
625 }
626 hasTarget = true;
627 }
628 if (appPackage_ != "") {
629 if (hasTarget) {
630 printf("--app %s options conflict, please check usage\n", appPackage_.c_str());
631 return false;
632 }
633 hasTarget = true;
634 }
635 if (!hasTarget && (controlCmd_.empty() || controlCmd_ == CONTROL_CMD_PREPARE)) {
636 printf("please select a target process\n");
637 return false;
638 }
639 if (controlCmd_ == CONTROL_CMD_PREPARE) {
640 CHECK_TRUE(CheckAppRestart(), false, 0, "");
641 }
642 return IsPidAndTidExist();
643 }
644
CheckTargetPids()645 bool SubCommandRecord::CheckTargetPids()
646 {
647 if (!IsAppRunning()) {
648 return false;
649 }
650 if (!selectPids_.empty()) {
651 for (auto pid : selectPids_) {
652 auto tids = GetSubthreadIDs(pid);
653 if (!tids.empty()) {
654 selectTids_.insert(selectTids_.end(), tids.begin(), tids.end());
655 mapPids_[pid] = tids;
656 }
657 }
658 }
659
660 CollectExcludeThread();
661 if (!SubCommand::HandleSubCommandExclude(excludeTidArgs_, excludeThreadNameArgs_, selectTids_)) {
662 return false;
663 }
664 selectPids_.insert(selectPids_.end(), selectTids_.begin(), selectTids_.end());
665
666 return true;
667 }
668
IsPidAndTidExist()669 bool SubCommandRecord::IsPidAndTidExist()
670 {
671 for (auto pid : selectPids_) {
672 if (!IsDir("/proc/" + std::to_string(pid))) {
673 printf("not exist pid %d\n", pid);
674 return false;
675 }
676 }
677 for (auto tid : selectTids_) {
678 if (!IsDir("/proc/" + std::to_string(tid))) {
679 printf("not exist tid %d\n", tid);
680 return false;
681 }
682 }
683 return true;
684 }
685
CheckReportOption()686 bool SubCommandRecord::CheckReportOption()
687 {
688 if (targetSystemWide_ && report_) {
689 printf("--report options conflict, please check usage\n");
690 return false;
691 }
692 return true;
693 }
694
CheckBacktrackOption()695 bool SubCommandRecord::CheckBacktrackOption()
696 {
697 CHECK_TRUE(backtrack_, true, 0, "");
698 if (controlCmd_.empty() && (clientPipeInput_ == -1)) {
699 printf("--backtrack must be used with --control\n");
700 return false;
701 }
702 CHECK_TRUE(!clockId_.empty(), true, 0, "");
703 if (GetClockId(clockId_) != CLOCK_BOOTTIME && GetClockId(clockId_) != CLOCK_MONOTONIC &&
704 GetClockId(clockId_) != CLOCK_MONOTONIC_RAW) {
705 printf("--backtrack not support the clockid\n");
706 return false;
707 }
708 return true;
709 }
710
CheckSpeOption()711 bool SubCommandRecord::CheckSpeOption()
712 {
713 constexpr uint64_t disable = 0;
714 constexpr uint64_t enable = 1;
715 const std::vector<std::string> optionNames = {"ts_enable", "pa_enable", "pct_enable",
716 "jitter", "branch_filter", "load_filter",
717 "store_filter"};
718 for (const auto &optionName : optionNames) {
719 if (CheckOutOfRange<uint64_t>(speOptMap_[optionName], disable, enable)) {
720 printf("Invalid %s value '%" PRIu64 "', value should be 0 or 1\n",
721 optionName.c_str(), speOptMap_[optionName]);
722 return false;
723 }
724 }
725 return true;
726 }
727
MsgPrintAndTrans(const bool isTrans,const std::string & msg)728 void SubCommandRecord::MsgPrintAndTrans(const bool isTrans, const std::string& msg)
729 {
730 if (isTrans && controlCmd_ == CONTROL_CMD_PREPARE) {
731 ChildResponseToMain(msg);
732 }
733 printf("%s", msg.c_str());
734 }
735
IsAppRestarted()736 bool SubCommandRecord::IsAppRestarted()
737 {
738 std::set<pid_t> oldPids {};
739 std::set<pid_t> newPids {};
740 std::vector<pid_t> intersection;
741 const auto startTime = steady_clock::now();
742 const auto endTime = startTime + std::chrono::seconds(CHECK_TIMEOUT);
743 HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect oldpids begin");
744 CollectPidsByAppname(oldPids, appPackage_);
745 for (auto it = oldPids.begin(); it != oldPids.end(); it++) {
746 HIPERF_HILOGI(MODULE_DEFAULT, "[IsAppRestarted] get oldpids %{public}d for app",
747 *it);
748 }
749 HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect oldpids finish");
750 std::string info = "please restart " + appPackage_ + " for profiling within 30 seconds\n";
751 MsgPrintAndTrans(true, info);
752 do {
753 HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect newPids begin");
754 CollectPidsByAppname(newPids, appPackage_);
755 for (auto it = newPids.begin(); it != newPids.end(); it++) {
756 HIPERF_HILOGI(MODULE_DEFAULT, "[IsAppRestarted] get newPids %{public}d for app",
757 *it);
758 }
759 HIPERF_HILOGI(MODULE_DEFAULT, "[CollectPidsByAppname] collect newPids finish");
760 std::set_intersection(oldPids.begin(), oldPids.end(),
761 newPids.begin(), newPids.end(), std::back_insert_iterator(intersection));
762 // app names are same, no intersection, means app restarted
763 CHECK_TRUE(!intersection.empty(), true, 0, "");
764 intersection.clear();
765 newPids.clear();
766 std::this_thread::sleep_for(milliseconds(CHECK_FREQUENCY));
767 } while (steady_clock::now() < endTime && !g_callStop.load());
768 std::string err = "app " + appPackage_ + " was not stopped within 30 seconds\n";
769 MsgPrintAndTrans(!g_callStop.load(), err);
770 return false;
771 }
772
CheckAppRestart()773 bool SubCommandRecord::CheckAppRestart()
774 {
775 if (!restart_) {
776 return true;
777 }
778 if (appPackage_.empty()) {
779 printf("to detect the performance of application startup, --app option must be given\n");
780 return false;
781 }
782 if (targetSystemWide_ || !selectPids_.empty()) {
783 printf("option --restart and -p/-a is conflict, please check usage\n");
784 return false;
785 }
786 return true;
787 }
788
GetPidFromAppPackage(const pid_t oldPid,const uint64_t waitAppTimeOut)789 pid_t SubCommandRecord::GetPidFromAppPackage(const pid_t oldPid, const uint64_t waitAppTimeOut)
790 {
791 pid_t res {-1};
792 const std::string basePath {"/proc/"};
793 const std::string cmdline {"/cmdline"};
794 const auto startTime = steady_clock::now();
795 const auto endTime = startTime + std::chrono::seconds(waitAppTimeOut);
796 do {
797 std::vector<std::string> subDirs = GetSubDirs(basePath);
798 for (const auto &subDir : subDirs) {
799 if (!IsDigits(subDir)) {
800 continue;
801 }
802 std::string fileName {basePath + subDir + cmdline};
803 if (IsSameCommand(ReadFileToString(fileName), appPackage_)) {
804 res = static_cast<pid_t>(std::stoul(subDir, nullptr));
805 HLOGD("[GetAppPackagePid]: get appPid for %s is %d", appPackage_.c_str(), res);
806 HIPERF_HILOGD(MODULE_DEFAULT, "[GetAppPackagePid] get appPid %{public}d for app",
807 res);
808 return res;
809 }
810 }
811 std::this_thread::sleep_for(milliseconds(checkAppMs_));
812 } while (steady_clock::now() < endTime && !g_callStop.load());
813 return res;
814 }
815
IsAppRunning()816 bool SubCommandRecord::IsAppRunning()
817 {
818 if (!appPackage_.empty()) {
819 pid_t appPid = GetPidFromAppPackage(-1, waitAppRunCheckTimeOut);
820 if (appPid <= 0) {
821 std::string err = "app " + appPackage_ + " not running\n";
822 MsgPrintAndTrans(!g_callStop.load(), err);
823 return false;
824 }
825 HLOGD("[CheckAppIsRunning] get appPid %d for app %s", appPid, appPackage_.c_str());
826 HIPERF_HILOGD(MODULE_DEFAULT, "[CheckAppIsRunning] get appPid %{public}d for app",
827 appPid);
828 selectPids_.push_back(appPid);
829 }
830 return true;
831 }
832
ParseDataLimitOption(const std::string & str)833 bool SubCommandRecord::ParseDataLimitOption(const std::string &str)
834 {
835 uint unit = 1;
836 char c = str.at(str.size() >= 1 ? str.size() - 1 : 0);
837 if (c == 'K' || c == 'k') {
838 unit = KILO;
839 } else if (c == 'm' || c == 'M') {
840 unit = KILO * KILO;
841 } else if (c == 'g' || c == 'G') {
842 unit = KILO * KILO * KILO;
843 } else {
844 return false;
845 }
846
847 std::string numStr = str.substr(0, str.size() >= 1 ? str.size() - 1 : 0);
848 unsigned long size = 0;
849 char *endPtr = nullptr;
850 errno = 0;
851 size = std::strtoul(numStr.c_str(), &endPtr, 10); // 10 : decimal scale
852 if (endPtr == numStr.c_str() || *endPtr != '\0' || errno != 0 || size == 0) {
853 HLOGE("num string convert to size failed, numStr: %s", numStr.c_str());
854 return false;
855 }
856
857 dataSizeLimit_ = size * unit;
858
859 return true;
860 }
861
ParseCallStackOption(const std::vector<std::string> & callStackType)862 bool SubCommandRecord::ParseCallStackOption(const std::vector<std::string> &callStackType)
863 {
864 if (callStackType.empty()) {
865 return true;
866 } else if (callStackType[0] == "fp") {
867 if (callStackType.size() != 1) {
868 printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str());
869 return false;
870 }
871 isCallStackFp_ = true;
872 } else if (callStackType[0] == "dwarf") {
873 if (callStackType.size() > MAX_DWARF_CALL_CHAIN) {
874 printf("Invalid -s value %s.\n", VectorToString(callStackType).c_str());
875 return false;
876 } else if (callStackType.size() == MAX_DWARF_CALL_CHAIN) {
877 char *endPtr = nullptr;
878 errno = 0;
879 unsigned long num = 0;
880 num = std::strtoul(callStackType.at(1).c_str(), &endPtr, 10); // 10 : decimal scale
881 if (endPtr == callStackType.at(1).c_str() || *endPtr != '\0' || errno > 0 || num > UINT_MAX) {
882 printf("Invalid -s value, dwarf stack size, '%s' is illegal.\n",
883 callStackType.at(1).c_str());
884 return false;
885 }
886 callStackDwarfSize_ = static_cast<uint32_t>(num);
887
888 if (callStackDwarfSize_ < MIN_SAMPLE_STACK_SIZE) {
889 printf("Invalid -s value, dwarf stack size, '%s' is too small.\n",
890 callStackType.at(1).c_str());
891 return false;
892 }
893 if (callStackDwarfSize_ > MAX_SAMPLE_STACK_SIZE) {
894 printf("Invalid -s value, dwarf stack size, '%s' is bigger than max value %u.\n",
895 callStackType.at(1).c_str(), MAX_SAMPLE_STACK_SIZE);
896 return false;
897 }
898 if ((callStackDwarfSize_ & MASK_ALIGNED_8) != 0) {
899 printf("Invalid -s value, dwarf stack size, '%s' is not 8 byte aligned.\n",
900 callStackType.at(1).c_str());
901 return false;
902 }
903 }
904 isCallStackDwarf_ = true;
905 SymbolsFile::needParseJsFunc_ = true; // only in record and dwarf mode need to parse
906 } else {
907 printf("Invalid -s value '%s'.\n", callStackType.at(0).c_str());
908 return false;
909 }
910 return true;
911 }
912
ParseBranchSampleType(const std::vector<std::string> & vecBranchSampleTypes)913 bool SubCommandRecord::ParseBranchSampleType(const std::vector<std::string> &vecBranchSampleTypes)
914 {
915 if (!vecBranchSampleTypes.empty()) {
916 for (auto &item : vecBranchSampleTypes) {
917 uint64_t type = GetBranchSampleType(item);
918 if (type != 0) {
919 branchSampleType_ |= type;
920 } else {
921 printf("Invalid -j value '%s'\n", item.c_str());
922 return false;
923 }
924 }
925 if ((branchSampleType_ & TYPE_PERF_SAMPLE_BRANCH) == 0) {
926 printf("Invalid -j value, requires at least one of "
927 "any, any_call, any_ret, ind_call, ind_jmp, cond, call.\n");
928 return false;
929 }
930 }
931 return true;
932 }
933
ParseControlCmd(const std::string cmd)934 bool SubCommandRecord::ParseControlCmd(const std::string cmd)
935 {
936 if (cmd.empty() || cmd == CONTROL_CMD_PREPARE || cmd == CONTROL_CMD_START || cmd == CONTROL_CMD_PAUSE ||
937 cmd == CONTROL_CMD_RESUME || cmd == CONTROL_CMD_STOP || cmd == CONTROL_CMD_OUTPUT) {
938 return true;
939 }
940
941 printf("Invalid --control %s option, command should be: prepare, start, pause, resume, output, stop.\n",
942 cmd.c_str());
943 return false;
944 }
945
SetPerfLimit(const std::string & file,int value,std::function<bool (int,int)> const & cmp,const std::string & param)946 bool SubCommandRecord::SetPerfLimit(const std::string& file, int value, std::function<bool (int, int)> const& cmp,
947 const std::string& param)
948 {
949 int oldValue = 0;
950 CHECK_TRUE(ReadIntFromProcFile(file, oldValue), false, LOG_TYPE_PRINTF, "read %s fail.\n", file.c_str());
951
952 if (cmp(oldValue, value)) {
953 HLOGI("cmp return true.");
954 return true;
955 }
956
957 if (IsRoot()) {
958 bool ret = WriteIntToProcFile(file, value);
959 if (!ret) {
960 printf("please set %s to %d manually if perf limit is wanted.\n", file.c_str(), value);
961 }
962 }
963
964 CHECK_TRUE(OHOS::system::SetParameter(param, std::to_string(value)), false, LOG_TYPE_PRINTF,
965 "set parameter %s fail.\n", param.c_str());
966 isNeedSetPerfHarden_ = true;
967 return true;
968 }
969
SetPerfCpuMaxPercent()970 bool SubCommandRecord::SetPerfCpuMaxPercent()
971 {
972 auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
973 return SetPerfLimit(PERF_CPU_TIME_MAX_PERCENT, cpuPercent_, cmp, "hiviewdfx.hiperf.perf_cpu_time_max_percent");
974 }
975
SetPerfMaxSampleRate()976 bool SubCommandRecord::SetPerfMaxSampleRate()
977 {
978 auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
979 int frequency = frequency_ != 0 ? frequency_ : PerfEvents::DEFAULT_SAMPLE_FREQUNCY;
980 int maxRate = 0;
981 CHECK_TRUE(ReadIntFromProcFile(PERF_EVENT_MAX_SAMPLE_RATE, maxRate), false, LOG_TYPE_PRINTF,
982 "read %s fail.\n", PERF_EVENT_MAX_SAMPLE_RATE.c_str());
983 if (maxRate > frequency) {
984 return true;
985 }
986 int newRate = frequency > static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE) ? frequency :
987 static_cast<int>(PerfEvents::DEFAULT_EVENT_MAX_SAMPLE_RATE);
988 return SetPerfLimit(PERF_EVENT_MAX_SAMPLE_RATE, newRate, cmp,
989 "hiviewdfx.hiperf.perf_event_max_sample_rate");
990 }
991
SetPerfEventMlock()992 bool SubCommandRecord::SetPerfEventMlock()
993 {
994 auto cmp = [](int oldValue, int newValue) { return oldValue == newValue; };
995 int mlock_kb = sysconf(_SC_NPROCESSORS_CONF) * (mmapPages_ + 1) * 4;
996 return SetPerfLimit(PERF_EVENT_MLOCK_KB, mlock_kb, cmp, "hiviewdfx.hiperf.perf_event_mlock_kb");
997 }
998
SetPerfHarden()999 bool SubCommandRecord::SetPerfHarden()
1000 {
1001 if (!isNeedSetPerfHarden_) {
1002 return true;
1003 }
1004
1005 std::string perfHarden = OHOS::system::GetParameter(PERF_DISABLE_PARAM, "1");
1006 if (perfHarden == "1") {
1007 CHECK_TRUE(OHOS::system::SetParameter(PERF_DISABLE_PARAM, "0"), false, LOG_TYPE_PRINTF,
1008 "set parameter security.perf_harden to 0 fail.");
1009 }
1010
1011 CHECK_TRUE(OHOS::system::SetParameter(PERF_DISABLE_PARAM, "1"), false, LOG_TYPE_PRINTF,
1012 "set parameter security.perf_harden to 1 fail.");
1013 return true;
1014 }
1015
TraceOffCpu()1016 bool SubCommandRecord::TraceOffCpu()
1017 {
1018 // whether system support sched_switch event
1019 int enable = -1;
1020 std::string node = SCHED_SWITCH;
1021 const std::string nodeDebug = SCHED_SWITCH_DEBUG;
1022 CHECK_TRUE(ReadIntFromProcFile(node.c_str(), enable) || ReadIntFromProcFile(nodeDebug.c_str(), enable),
1023 false, LOG_TYPE_PRINTF, "Cannot trace off CPU, event sched:sched_switch is not available (%s or %s)\n",
1024 node.c_str(), nodeDebug.c_str());
1025
1026 return true;
1027 }
1028
SetSavedCmdlinesSize()1029 void SubCommandRecord::SetSavedCmdlinesSize()
1030 {
1031 if (!ReadIntFromProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) {
1032 printf("Failed to read from %s.\n", SAVED_CMDLINES_SIZE.c_str());
1033 }
1034 if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, cmdlinesSize_)) {
1035 printf("Failed to write size:%d to %s.\n", cmdlinesSize_, SAVED_CMDLINES_SIZE.c_str());
1036 }
1037 }
1038
RecoverSavedCmdlinesSize()1039 void SubCommandRecord::RecoverSavedCmdlinesSize()
1040 {
1041 CHECK_TRUE(oldCmdlinesSize_ != 0, NO_RETVAL, 0, "");
1042 if (!WriteIntToProcFile(SAVED_CMDLINES_SIZE, oldCmdlinesSize_)) {
1043 printf("Failed to recover value of %s.\n", SAVED_CMDLINES_SIZE.c_str());
1044 }
1045 }
1046
PreparePerfEvent()1047 bool SubCommandRecord::PreparePerfEvent()
1048 {
1049 // we need to notify perfEvents_ sampling mode by SetRecordCallBack first
1050 auto processRecord = [this](PerfEventRecord& record) -> bool {
1051 return this->ProcessRecord(record);
1052 };
1053 perfEvents_.SetRecordCallBack(processRecord);
1054
1055 if (selectEvents_.size() > 0 && selectEvents_[0] == "arm_spe_0") {
1056 if (!IsRoot()) {
1057 printf("%s options needs root privilege, please check usage\n", selectEvents_[0].c_str());
1058 return false;
1059 }
1060 selectEvents_.insert(selectEvents_.begin(), "sw-dummy");
1061 perfEvents_.isSpe_ = true;
1062 perfEvents_.SetConfig(speOptMap_);
1063 isSpe_ = true;
1064 }
1065
1066 perfEvents_.SetCpu(selectCpus_);
1067 perfEvents_.SetPid(selectPids_); // Tids has insert Pids in CheckTargetProcessOptions()
1068
1069 perfEvents_.SetSystemTarget(targetSystemWide_);
1070 perfEvents_.SetTimeOut(timeStopSec_);
1071 perfEvents_.SetVerboseReport(verboseReport_);
1072 perfEvents_.SetMmapPages(mmapPages_);
1073 if (isCallStackFp_) {
1074 perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::FP);
1075 } else if (isCallStackDwarf_) {
1076 perfEvents_.SetSampleStackType(PerfEvents::SampleStackType::DWARF);
1077 perfEvents_.SetDwarfSampleStackSize(callStackDwarfSize_);
1078 }
1079 if (!perfEvents_.SetBranchSampleType(branchSampleType_)) {
1080 printf("branch sample %s is not supported\n", VectorToString(vecBranchFilters_).c_str());
1081 HLOGE("Fail to SetBranchSampleType %" PRIx64 "", branchSampleType_);
1082 HIPERF_HILOGE(MODULE_DEFAULT, "[PreparePerfEvent] Fail to SetBranchSampleType %{public}" PRIx64 "",
1083 branchSampleType_);
1084 return false;
1085 }
1086 if (!clockId_.empty()) {
1087 perfEvents_.SetClockId(GetClockId(clockId_));
1088 }
1089
1090 if (frequency_ > 0) {
1091 perfEvents_.SetSampleFrequency(frequency_);
1092 } else if (period_ > 0) {
1093 perfEvents_.SetSamplePeriod(period_);
1094 }
1095
1096 perfEvents_.SetBackTrack(backtrack_);
1097 perfEvents_.SetBackTrackTime(backtrackTime_);
1098
1099 perfEvents_.SetInherit(!noInherit_);
1100 perfEvents_.SetTrackedCommand(trackedCommand_);
1101
1102 // set default sample event
1103 if (selectEvents_.empty() && selectGroups_.empty()) {
1104 selectEvents_.push_back("hw-cpu-cycles");
1105 }
1106
1107 CHECK_TRUE(perfEvents_.AddEvents(selectEvents_), false, 1, "Fail to AddEvents events");
1108 for (auto &group : selectGroups_) {
1109 CHECK_TRUE(perfEvents_.AddEvents(group, true), false, 1, "Fail to AddEvents groups");
1110 }
1111 // cpu off add after default event (we need both sched_switch and user selected events)
1112 if (offCPU_) {
1113 CHECK_TRUE(std::find(selectEvents_.begin(), selectEvents_.end(), "sched_switch") == selectEvents_.end(),
1114 false, LOG_TYPE_PRINTF, "--offcpu is not supported event sched_switch\n");
1115 // insert a sched_switch event to trace offcpu event
1116 CHECK_TRUE(perfEvents_.AddOffCpuEvent(), false, 1, "Fail to AddEOffCpuvent");
1117 }
1118
1119 return true;
1120 }
1121
PrepareSysKernel()1122 bool SubCommandRecord::PrepareSysKernel()
1123 {
1124 SetHM();
1125 SetSavedCmdlinesSize();
1126 CHECK_TRUE(SetPerfMaxSampleRate(), false, 1, "Fail to call SetPerfMaxSampleRate(%d)", frequency_);
1127
1128 CHECK_TRUE(SetPerfCpuMaxPercent(), false, 1, "Fail to set perf event cpu limit to %d\n", cpuPercent_);
1129
1130 CHECK_TRUE(SetPerfEventMlock(), false, 1, "Fail to set perf event mlock limit\n");
1131
1132 CHECK_TRUE(SetPerfHarden(), false, 1, "Fail to set perf event harden\n");
1133
1134 CHECK_TRUE(!offCPU_ || TraceOffCpu(), false, 1, "Fail to TraceOffCpu");
1135
1136 return true;
1137 }
1138
PrepareKernelMaps()1139 void SubCommandRecord::PrepareKernelMaps()
1140 {
1141 // load vsdo first
1142 virtualRuntime_.LoadVdso();
1143
1144 if (!callChainUserOnly_) {
1145 // prepare from kernel and ko
1146 virtualRuntime_.SetNeedKernelCallChain(!callChainUserOnly_);
1147 virtualRuntime_.UpdateKernelSpaceMaps();
1148 if (isRoot_) {
1149 virtualRuntime_.UpdateKernelModulesSpaceMaps();
1150 }
1151 if (isHM_) {
1152 virtualRuntime_.UpdateServiceSpaceMaps();
1153 }
1154 }
1155
1156 if (isHM_) {
1157 virtualRuntime_.UpdateDevhostSpaceMaps();
1158 }
1159 }
1160
PrepareVirtualRuntime()1161 bool SubCommandRecord::PrepareVirtualRuntime()
1162 {
1163 auto saveRecord = [this](PerfEventRecord& record) -> bool {
1164 if (!isRoot_) {
1165 UpdateDevHostMaps(record);
1166 }
1167 return this->SaveRecord(record);
1168 };
1169 virtualRuntime_.SetRecordMode(saveRecord);
1170
1171 // do some config for virtualRuntime_
1172 virtualRuntime_.SetCallStackExpend(disableCallstackExpend_ ? 0 : 1);
1173 // these is same for virtual runtime
1174 virtualRuntime_.SetDisableUnwind(disableUnwind_ || delayUnwind_);
1175 virtualRuntime_.EnableDebugInfoSymbolic(enableDebugInfoSymbolic_);
1176 if (!symbolDir_.empty()) {
1177 if (!virtualRuntime_.SetSymbolsPaths(symbolDir_)) {
1178 printf("Failed to set symbol path(%s)\n", VectorToString(symbolDir_).c_str());
1179 return false;
1180 }
1181 }
1182
1183 PrepareKernelMaps();
1184 if (dedupStack_) {
1185 virtualRuntime_.SetDedupStack();
1186 auto collectSymbol = [this](PerfRecordSample *sample) {
1187 this->CollectSymbol(std::move(sample));
1188 };
1189 virtualRuntime_.SetCollectSymbolCallBack(collectSymbol);
1190 }
1191 return true;
1192 }
1193
WriteCommEventBeforeSampling()1194 void SubCommandRecord::WriteCommEventBeforeSampling()
1195 {
1196 CHECK_TRUE(!restart_, NO_RETVAL, 0, "");
1197 CHECK_TRUE(!backtrack_, NO_RETVAL, 0, "");
1198 for (auto it = mapPids_.begin(); it != mapPids_.end(); ++it) {
1199 virtualRuntime_.GetThread(it->first, it->first);
1200 for (auto tid : it->second) {
1201 virtualRuntime_.GetThread(it->first, tid);
1202 }
1203 }
1204 if (isSpe_ && mapPids_.empty() && !selectPids_.empty()) {
1205 for (auto pid : selectPids_) {
1206 virtualRuntime_.GetThread(pid, pid);
1207 }
1208 }
1209 }
1210
ClientCommandResponse(const bool response)1211 bool SubCommandRecord::ClientCommandResponse(const bool response)
1212 {
1213 return ClientCommandResponse(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL);
1214 }
1215
ClientCommandResponse(const std::string & str)1216 bool SubCommandRecord::ClientCommandResponse(const std::string& str)
1217 {
1218 if (!isHiperfClient_) {
1219 clientPipeOutput_ = open(fifoFileS2C_.c_str(), O_WRONLY);
1220 if (clientPipeOutput_ == -1) {
1221 char errInfo[ERRINFOLEN] = { 0 };
1222 strerror_r(errno, errInfo, ERRINFOLEN);
1223 HLOGE("open fifo file(%s) failed, errno:%d:%s", fifoFileS2C_.c_str(), errno, errInfo);
1224 HIPERF_HILOGE(MODULE_DEFAULT, "open fifo file(%{public}s) failed, errno:%{public}d:%{public}s",
1225 fifoFileS2C_.c_str(), errno, errInfo);
1226 return false;
1227 }
1228 }
1229 ssize_t size = write(clientPipeOutput_, str.c_str(), str.size());
1230 if (size != static_cast<ssize_t>(str.size())) {
1231 char errInfo[ERRINFOLEN] = { 0 };
1232 strerror_r(errno, errInfo, ERRINFOLEN);
1233 HLOGD("Server:%s -> %d : %zd %d:%s", str.c_str(), clientPipeOutput_, size, errno, errInfo);
1234 HIPERF_HILOGE(MODULE_DEFAULT, "Server:%{public}s -> %{public}d : %{public}zd %{public}d:%{public}s",
1235 str.c_str(), clientPipeOutput_, size, errno, errInfo);
1236 return false;
1237 }
1238 return true;
1239 }
1240
ChildResponseToMain(const bool response)1241 bool SubCommandRecord::ChildResponseToMain(const bool response)
1242 {
1243 return ChildResponseToMain(response ? HiperfClient::ReplyOK : HiperfClient::ReplyFAIL);
1244 }
1245
ChildResponseToMain(const std::string & str)1246 bool SubCommandRecord::ChildResponseToMain(const std::string& str)
1247 {
1248 int tempFd = isHiperfClient_ ? clientPipeOutput_ : writeFd_;
1249 ssize_t size = write(tempFd, str.c_str(), str.size());
1250 if (size != static_cast<ssize_t>(str.size())) {
1251 char errInfo[ERRINFOLEN] = { 0 };
1252 strerror_r(errno, errInfo, ERRINFOLEN);
1253 HLOGE("write pipe failed. str:%s, size:%zd, errno:%d:%s", str.c_str(), size, errno, errInfo);
1254 HIPERF_HILOGE(MODULE_DEFAULT,
1255 "write pipe failed. str:%{public}s, size:%{public}zd, errno:%{public}d:%{public}s",
1256 str.c_str(), size, errno, errInfo);
1257 return false;
1258 }
1259 return true;
1260 }
1261
MainRecvFromChild(const int fd,std::string & reply)1262 bool SubCommandRecord::MainRecvFromChild(const int fd, std::string& reply)
1263 {
1264 struct pollfd pollFd {
1265 fd, POLLIN, 0
1266 };
1267 int polled = poll(&pollFd, 1, CONTROL_WAIT_RESPONSE_TIMEOUT);
1268 reply.clear();
1269 if (polled > 0) {
1270 bool exitLoop = false;
1271 while (!exitLoop) {
1272 char c;
1273 ssize_t result = TEMP_FAILURE_RETRY(read(fd, &c, 1));
1274 if (result <= 0) {
1275 HLOGD("read from pipeFd failed");
1276 HIPERF_HILOGE(MODULE_DEFAULT, "read from pipeFd failed");
1277 exitLoop = true;
1278 }
1279 reply.push_back(c);
1280 if (c == '\n') {
1281 exitLoop = true;
1282 }
1283 }
1284 } else if (polled == 0) {
1285 HLOGD("wait pipeFd timeout");
1286 HIPERF_HILOGE(MODULE_DEFAULT, "wait pipeFd timeout");
1287 return false;
1288 } else {
1289 HLOGD("wait pipeFd failed");
1290 HIPERF_HILOGE(MODULE_DEFAULT, "wait pipeFd failed");
1291 return false;
1292 }
1293 return true;
1294 }
1295
IsSamplingRunning()1296 bool SubCommandRecord::IsSamplingRunning()
1297 {
1298 constexpr int maxWaitTrackingCount = 3000 / 100; // wait 3 second
1299 int waitTrackingCount = maxWaitTrackingCount;
1300 while (!perfEvents_.IsTrackRunning()) {
1301 waitTrackingCount--;
1302 if (waitTrackingCount <= 0) {
1303 return false;
1304 }
1305 constexpr uint64_t waitTrackingSleepMs = 100;
1306 std::this_thread::sleep_for(milliseconds(waitTrackingSleepMs));
1307 }
1308 return true;
1309 }
1310
PreOutputRecordFile()1311 bool SubCommandRecord::PreOutputRecordFile()
1312 {
1313 if (!backtrack_) {
1314 HLOGE("not backtrack mode");
1315 return false;
1316 }
1317 if (perfEvents_.IsOutputTracking()) {
1318 HLOGE("output track is in process");
1319 return false;
1320 }
1321 if (!CreateInitRecordFile(false)) {
1322 HLOGE("create record file before output");
1323 return false;
1324 }
1325 PrepareKernelMaps();
1326 if (!perfEvents_.OutputTracking()) {
1327 HLOGE("enable output tracking failed");
1328 return false;
1329 }
1330 outputEnd_ = false;
1331 perfPipe_.SetOutPutEnd(outputEnd_);
1332 return true;
1333 }
1334
OutputRecordFile()1335 void SubCommandRecord::OutputRecordFile()
1336 {
1337 uint32_t loopCount = 0;
1338 while (perfEvents_.IsOutputTracking()) {
1339 std::this_thread::sleep_for(milliseconds(CHECK_WAIT_TIME_MS));
1340 if (loopCount++ > MAX_SERVER_OUTPUT_WAIT_COUNT) {
1341 HLOGE("wait time out");
1342 perfEvents_.SetOutputTrackingStatus(false);
1343 break;
1344 }
1345 }
1346
1347 if (!FinishWriteRecordFile()) {
1348 HLOGE("output record failed");
1349 }
1350 fileWriter_ = nullptr;
1351 }
1352
PostOutputRecordFile(const bool output)1353 bool SubCommandRecord::PostOutputRecordFile(const bool output)
1354 {
1355 if (output) {
1356 OutputRecordFile();
1357 }
1358
1359 fileWriter_ = nullptr;
1360 outputEnd_ = true;
1361 perfPipe_.SetOutPutEnd(outputEnd_);
1362 StringViewHold::Get().Clean();
1363 return true;
1364 }
1365
InitControlCommandHandlerMap()1366 void SubCommandRecord::InitControlCommandHandlerMap()
1367 {
1368 controlCommandHandlerMap_.clear();
1369 controlCommandHandlerMap_.emplace(HiperfClient::ReplyStart, ControlCommandHandler{
1370 std::bind(&PerfEvents::EnableTracking, &perfEvents_)
1371 });
1372
1373 controlCommandHandlerMap_.emplace(HiperfClient::ReplyCheck, ControlCommandHandler{
1374 std::bind(&SubCommandRecord::clientRunning_, this)
1375 });
1376
1377 controlCommandHandlerMap_.emplace(HiperfClient::ReplyStop, ControlCommandHandler{
1378 std::bind(&PerfEvents::StopTracking, &perfEvents_)
1379 });
1380
1381 controlCommandHandlerMap_.emplace(HiperfClient::ReplyPause, ControlCommandHandler{
1382 std::bind(&PerfEvents::PauseTracking, &perfEvents_)
1383 });
1384
1385 controlCommandHandlerMap_.emplace(HiperfClient::ReplyResume, ControlCommandHandler{
1386 std::bind(&PerfEvents::ResumeTracking, &perfEvents_)
1387 });
1388
1389 controlCommandHandlerMap_.emplace(HiperfClient::ReplyOutput, ControlCommandHandler{
1390 std::bind(&SubCommandRecord::PreOutputRecordFile, this),
1391 std::bind(&SubCommandRecord::PostOutputRecordFile, this, std::placeholders::_1)
1392 });
1393
1394 controlCommandHandlerMap_.emplace(HiperfClient::ReplyOutputCheck, ControlCommandHandler{
1395 std::bind(&SubCommandRecord::outputEnd_, this)
1396 });
1397 }
1398
CreateReplyThread()1399 inline void SubCommandRecord::CreateReplyThread()
1400 {
1401 replyCommandHandle_ = std::thread(&SubCommandRecord::ReplyCommandHandle, this);
1402 }
1403
ReplyCommandHandle()1404 void SubCommandRecord::ReplyCommandHandle()
1405 {
1406 if (!IsSamplingRunning()) {
1407 HLOGI("IsSamplingRunning() return false");
1408 HIPERF_HILOGI(MODULE_DEFAULT, "IsSamplingRunning() return false");
1409 ChildResponseToMain(false);
1410 isHiperfClient_ = false;
1411 return;
1412 }
1413 ChildResponseToMain(true);
1414 }
1415
CreateClientThread()1416 inline void SubCommandRecord::CreateClientThread()
1417 {
1418 // make a thread wait the other command
1419 clientCommandHandle_ = std::thread(&SubCommandRecord::ClientCommandHandle, this);
1420 }
1421
ClientCommandHandle()1422 void SubCommandRecord::ClientCommandHandle()
1423 {
1424 using namespace HiperfClient;
1425 InitControlCommandHandlerMap();
1426
1427 bool hasRead = true;
1428 while (clientRunning_.load()) {
1429 if (isFifoServer_ && hasRead) {
1430 if (clientPipeInput_ != -1) {
1431 // after read(), block is disabled, the poll will be waked neven if no data
1432 close(clientPipeInput_);
1433 }
1434 clientPipeInput_ = open(fifoFileC2S_.c_str(), O_RDONLY | O_NONBLOCK);
1435 }
1436 struct pollfd pollFd {
1437 clientPipeInput_, POLLIN, 0
1438 };
1439 int polled = poll(&pollFd, 1, CONTROL_WAITREPY_TIMEOUT.count());
1440 if (polled <= 0) {
1441 hasRead = false;
1442 continue;
1443 }
1444 hasRead = true;
1445 std::string command;
1446 while (true) {
1447 char c;
1448 ssize_t result = TEMP_FAILURE_RETRY(read(clientPipeInput_, &c, 1));
1449 if (result <= 0) {
1450 HLOGD("server :read from pipe file failed");
1451 HIPERF_HILOGD(MODULE_DEFAULT, "[ClientCommandHandle] server :read from pipe file failed");
1452 break;
1453 }
1454 command.push_back(c);
1455 if (c == '\n') {
1456 break;
1457 }
1458 }
1459 HLOGD("server:new command %s", command.c_str());
1460 HIPERF_HILOGI(MODULE_DEFAULT, "[ClientCommandHandle] server:new command : %{public}s", command.c_str());
1461 if (command.find("STOP") != std::string::npos) {
1462 HLOGD("receive sop command, set g_callStop to true");
1463 HIPERF_HILOGI(MODULE_DEFAULT, "[ClientCommandHandle] Handlereceive sop command, set g_callStop to true");
1464 g_callStop.store(true);
1465 }
1466 DispatchControlCommand(command);
1467 }
1468 }
1469
DispatchControlCommand(const std::string & command)1470 void SubCommandRecord::DispatchControlCommand(const std::string& command)
1471 {
1472 auto it = controlCommandHandlerMap_.find(command);
1473 if (it == controlCommandHandlerMap_.end()) {
1474 return;
1475 }
1476
1477 ControlCommandHandler& handler = it->second;
1478 bool ret = handler.preProcess();
1479 ClientCommandResponse(ret);
1480 handler.postProcess(ret);
1481 }
1482
ProcessControl()1483 bool SubCommandRecord::ProcessControl()
1484 {
1485 if (controlCmd_.empty()) {
1486 return true;
1487 }
1488 HIPERF_HILOGI(MODULE_DEFAULT, "control cmd : %{public}s", controlCmd_.c_str());
1489 perfPipe_.SetFifoFileName(CommandType::RECORD, controlCmd_, fifoFileC2S_, fifoFileS2C_);
1490 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1491 CHECK_TRUE(CreateFifoServer(), false, 0, "");
1492 return true;
1493 }
1494
1495 isFifoClient_ = true;
1496 return perfPipe_.ProcessControlCmd();
1497 }
1498
RemoveFifoFile()1499 void SubCommandRecord::RemoveFifoFile()
1500 {
1501 char errInfo[ERRINFOLEN] = { 0 };
1502 if (remove(fifoFileC2S_.c_str()) != 0) {
1503 strerror_r(errno, errInfo, ERRINFOLEN);
1504 HLOGE("remove %s failed, errno:(%d:%s)", fifoFileC2S_.c_str(), errno, errInfo);
1505 HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)",
1506 fifoFileC2S_.c_str(), errno, errInfo);
1507 }
1508 if (remove(fifoFileS2C_.c_str()) != 0) {
1509 strerror_r(errno, errInfo, ERRINFOLEN);
1510 HLOGE("remove %s failed, errno:(%d:%s)", fifoFileS2C_.c_str(), errno, errInfo);
1511 HIPERF_HILOGE(MODULE_DEFAULT, "remove %{public}s failed, errno:(%{public}d:%{public}s)",
1512 fifoFileS2C_.c_str(), errno, errInfo);
1513 }
1514 }
1515
CreateFifoServer()1516 bool SubCommandRecord::CreateFifoServer()
1517 {
1518 char errInfo[ERRINFOLEN] = { 0 };
1519 if (!perfPipe_.CreateFifoFile()) {
1520 return false;
1521 }
1522
1523 int pipeFd[2];
1524 if (pipe(pipeFd) == -1) {
1525 strerror_r(errno, errInfo, ERRINFOLEN);
1526 HLOGE("pipe creation error, errno:(%d:%s)", errno, errInfo);
1527 HIPERF_HILOGE(MODULE_DEFAULT, "pipe creation error, errno:(%{public}d:%{public}s)", errno, errInfo);
1528 RemoveFifoFile();
1529 return false;
1530 }
1531
1532 CheckIpcBeforeFork();
1533 pid_t pid = fork();
1534 allowIpc_ = true;
1535
1536 if (pid == -1) {
1537 strerror_r(errno, errInfo, ERRINFOLEN);
1538 HLOGE("fork failed. %d:%s", errno, errInfo);
1539 HIPERF_HILOGE(MODULE_DEFAULT, "[CreateFifoServer] fork failed. %{public}d:%{public}s", errno, errInfo);
1540 close(pipeFd[PIPE_READ]);
1541 close(pipeFd[PIPE_WRITE]);
1542 return false;
1543 } else if (pid == 0) { // child process
1544 close(STDIN_FILENO);
1545 close(STDERR_FILENO);
1546 close(pipeFd[PIPE_READ]);
1547 writeFd_ = pipeFd[PIPE_WRITE];
1548 isFifoServer_ = true;
1549 nullFd_ = open("/dev/null", O_WRONLY);
1550 (void)dup2(nullFd_, STDOUT_FILENO); // redirect stdout to /dev/null
1551 } else { // parent process
1552 close(pipeFd[PIPE_WRITE]);
1553 readFd_ = pipeFd[PIPE_READ];
1554 isFifoClient_ = true;
1555 bool isSuccess = false;
1556 bool isPrint = false;
1557 const auto startTime = steady_clock::now();
1558 const auto endTime = startTime + std::chrono::seconds(WAIT_TIMEOUT);
1559 do {
1560 std::string reply = "";
1561 bool ret = MainRecvFromChild(readFd_, reply);
1562 if (ret && reply.find("OK") != std::string::npos) {
1563 printf("%s control hiperf sampling success.\n", restart_ ? "start" : "create");
1564 isSuccess = true;
1565 break;
1566 }
1567 HLOGE("reply is (%s)", reply.c_str());
1568 HIPERF_HILOGE(MODULE_DEFAULT, "reply is (%{public}s)", reply.c_str());
1569 if (ret && reply.find("FAIL") == std::string::npos) {
1570 printf("%s", reply.c_str());
1571 if (reply.find("debug application") != std::string::npos) {
1572 isPrint = true;
1573 }
1574 continue;
1575 }
1576 if (ret && reply.find("FAIL") != std::string::npos) {
1577 break;
1578 }
1579 if (!ret) {
1580 isPrint = true;
1581 break;
1582 }
1583 } while (steady_clock::now() < endTime);
1584 if (!isSuccess) {
1585 kill(pid, SIGKILL);
1586 RemoveFifoFile();
1587 if (isPrint) {
1588 strerror_r(errno, errInfo, ERRINFOLEN);
1589 printf("create control hiperf sampling failed. %d:%s\n", errno, errInfo);
1590 return false;
1591 }
1592 }
1593 }
1594 return true;
1595 }
1596
OnSubCommand(std::vector<std::string> & args)1597 HiperfError SubCommandRecord::OnSubCommand(std::vector<std::string>& args)
1598 {
1599 HIPERF_HILOGI(MODULE_DEFAULT, "SubCommandRecord onSubCommand start");
1600 if (!ProcessControl()) {
1601 return HiperfError::PROCESS_CONTROL_FAIL;
1602 } else if (isFifoClient_) {
1603 return HiperfError::NO_ERR;
1604 }
1605 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] ProcessControl finish");
1606 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1607 CreateClientThread();
1608 if (!appPackage_.empty() && restart_) {
1609 if (!IsAppRestarted()) {
1610 ChildResponseToMain(false);
1611 CloseClientThread();
1612 return HiperfError::CHECK_RESTART_OPTION_FAIL;
1613 }
1614 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] App restart success");
1615 }
1616 }
1617
1618 if (!isRoot_) {
1619 offset_ = GetOffsetNum();
1620 SymbolsFile::offsetNum_ = offset_;
1621 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] get offset success");
1622 }
1623
1624 if (!CheckTargetPids()) {
1625 HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] CheckTargetPids failed");
1626 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1627 ChildResponseToMain(false);
1628 CloseClientThread();
1629 }
1630 if (controlCmd_.empty()) {
1631 return HiperfError::NO_ERR;
1632 } else {
1633 return HiperfError::CHECK_OPTION_PID_FAIL;
1634 }
1635 }
1636 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] CheckTargetPids success");
1637 std::string err = OHOS::Developtools::HiPerf::HandleAppInfo(appPackage_, inputPidTidArgs_);
1638 if (!err.empty()) {
1639 ChildResponseToMain(err);
1640 ChildResponseToMain(false);
1641 CloseClientThread();
1642 return HiperfError::CHECK_DEBUG_APP_FAIL;
1643 }
1644 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] HandleAppInfo finish");
1645 // prepare PerfEvents
1646 if (!PrepareSysKernel()) {
1647 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1648 ChildResponseToMain(false);
1649 CloseClientThread();
1650 }
1651 return HiperfError::PREPARE_SYS_KERNEL_FAIL;
1652 }
1653 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] PrepareSysKernel finish");
1654 if (!PreparePerfEvent()) {
1655 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1656 ChildResponseToMain(false);
1657 CloseClientThread();
1658 }
1659 return HiperfError::PREPARE_PERF_EVENT_FAIL;
1660 }
1661 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] PreparePerfEvent finish");
1662 // prepar some attr before CreateInitRecordFile
1663 if (!perfEvents_.PrepareTracking()) {
1664 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1665 ChildResponseToMain(false);
1666 CloseClientThread();
1667 }
1668 HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] Fail to prepare tracking");
1669 HLOGE("Fail to prepare tracking");
1670 return HiperfError::PREPARE_TACKING_FAIL;
1671 }
1672 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord perfEvents prepared");
1673
1674 if (!backtrack_ && !CreateInitRecordFile(delayUnwind_ ? false : compressData_)) {
1675 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1676 ChildResponseToMain(false);
1677 CloseClientThread();
1678 }
1679 HLOGE("Fail to create record file %s", outputFilename_.c_str());
1680 HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] Fail to create record file");
1681 return HiperfError::CREATE_OUTPUT_FILE_FAIL;
1682 }
1683 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] CreateInitRecordFile finished");
1684 if (!PrepareVirtualRuntime()) {
1685 if (controlCmd_ == CONTROL_CMD_PREPARE) {
1686 ChildResponseToMain(false);
1687 CloseClientThread();
1688 }
1689 HLOGE("Fail to prepare virtualRuntime");
1690 HIPERF_HILOGE(MODULE_DEFAULT, "[OnSubCommand] Fail to prepare virtualRuntime");
1691 return HiperfError::PREPARE_VIRTUAL_RUNTIME_FAIL;
1692 }
1693
1694 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord virtualRuntime prepared");
1695
1696 if (controlCmd_ == CONTROL_CMD_PREPARE || isHiperfClient_) {
1697 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] CreateReplyThread start");
1698 CreateReplyThread();
1699 }
1700
1701 if (isHiperfClient_) {
1702 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] isHiperfClient_ CreateClientThread start");
1703 CreateClientThread();
1704 }
1705 //write comm event
1706 WriteCommEventBeforeSampling();
1707 SetExcludeHiperf();
1708 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord StartTracking");
1709 // if mmap record size has been larger than limit, dont start sampling.
1710 if (!isDataSizeLimitStop_) {
1711 if (restart_ && controlCmd_ == CONTROL_CMD_PREPARE) {
1712 RETURN_IF(!perfEvents_.StartTracking(isFifoServer_), HiperfError::PREPARE_START_TRACKING_FAIL);
1713 } else {
1714 RETURN_IF(!perfEvents_.StartTracking((!isFifoServer_) && (clientPipeInput_ == -1)),
1715 HiperfError::START_TRACKING_FAIL);
1716 }
1717 }
1718 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord perfEvents tracking finish");
1719
1720 if (isSpe_) {
1721 HLOGD("stop write spe record");
1722 fileWriter_->SetWriteRecordStat(false);
1723 }
1724 startSaveFileTimes_ = steady_clock::now();
1725 if (!backtrack_) {
1726 if (!FinishWriteRecordFile()) {
1727 HLOGE("Fail to finish record file %s", outputFilename_.c_str());
1728 HIPERF_HILOGE(MODULE_DEFAULT, "Fail to finish record file");
1729 return HiperfError::FINISH_WRITE_RECORD_FILE_FAIL;
1730 } else if (!PostProcessRecordFile()) {
1731 HLOGE("Fail to post process record file");
1732 HIPERF_HILOGE(MODULE_DEFAULT, "Fail to post process record file");
1733 return HiperfError::POST_PROCESS_RECORD_FILE;
1734 }
1735 RecordCompleted();
1736 }
1737
1738 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord final report");
1739 // finial report
1740 RecoverSavedCmdlinesSize();
1741 OnlineReportData();
1742 CloseReplyThread();
1743 CloseClientThread();
1744 RemoveVdsoTmpFile();
1745 AgeHiperflogFiles();
1746 HIPERF_HILOGI(MODULE_DEFAULT, "[OnSubCommand] SubCommandRecord finish");
1747 return HiperfError::NO_ERR;
1748 }
1749
CloseClientThread()1750 void SubCommandRecord::CloseClientThread()
1751 {
1752 if (clientCommandHandle_.joinable()) {
1753 clientRunning_.store(false);
1754 HLOGI("CloseClientThread");
1755 if (nullFd_ != -1) {
1756 close(nullFd_);
1757 }
1758 clientCommandHandle_.join();
1759 close(clientPipeInput_);
1760 close(clientPipeOutput_);
1761 if (isFifoServer_) {
1762 RemoveFifoFile();
1763 }
1764 }
1765 }
1766
CloseReplyThread()1767 void SubCommandRecord::CloseReplyThread()
1768 {
1769 if (replyCommandHandle_.joinable()) {
1770 clientRunning_.store(false);
1771 HLOGI("CloseReplyThread");
1772 replyCommandHandle_.join();
1773 }
1774 }
1775
RemoveVdsoTmpFile()1776 void SubCommandRecord::RemoveVdsoTmpFile()
1777 {
1778 std::vector<std::string> fileName = {"/data/log/hiperflog/[shmm]", "/data/log/hiperflog/[vdso]"};
1779 for (auto name : fileName) {
1780 if (access(name.c_str(), F_OK) == 0) {
1781 if (remove(name.c_str()) != 0) {
1782 char errInfo[ERRINFOLEN] = { 0 };
1783 strerror_r(errno, errInfo, ERRINFOLEN);
1784 HLOGE("remove file %s failed,errno:%d,errinfo:%s", name.c_str(), errno, errInfo);
1785 }
1786 }
1787 }
1788 }
1789
UpdateDevHostMapsAndIPs(PerfEventRecord & record)1790 void SubCommandRecord::UpdateDevHostMapsAndIPs(PerfEventRecord& record)
1791 {
1792 if (isRoot_) {
1793 return;
1794 }
1795 UpdateDevHostCallChains(record);
1796 UpdateDevHostMaps(record);
1797 }
1798
ProcessRecord(PerfEventRecord & record)1799 bool SubCommandRecord::ProcessRecord(PerfEventRecord& record)
1800 {
1801 CHECK_TRUE(record.GetName() != nullptr, false, 1, "record is null");
1802 #if HIDEBUG_RECORD_NOT_PROCESS
1803 // some times we want to check performance
1804 // but we still want to see the record number
1805 if (record.GetType() == PERF_RECORD_SAMPLE) {
1806 recordSamples_++;
1807 } else {
1808 recordNoSamples_++;
1809 }
1810 return true;
1811 #else
1812 #ifdef HIPERF_DEBUG_TIME
1813 const auto startTime = steady_clock::now();
1814 #endif
1815 if (record.GetType() == PERF_RECORD_SAMPLE) {
1816 PerfRecordSample& recordSample = static_cast<PerfRecordSample&>(record);
1817 if (IsThreadExcluded(recordSample.data_.pid, recordSample.data_.tid)) {
1818 return true;
1819 }
1820 } else if (record.GetType() == PERF_RECORD_COMM) {
1821 PerfRecordComm& recordComm = static_cast<PerfRecordComm&>(record);
1822 for (const auto& threadName : excludeThreadNameArgs_) {
1823 if (threadName.compare(recordComm.data_.comm) == 0) {
1824 excludeTids_.insert(recordComm.data_.tid);
1825 break;
1826 }
1827 }
1828 }
1829
1830 if (backtrack_ && !perfEvents_.IsOutputTracking()) {
1831 return true;
1832 }
1833
1834 // May create some simulated events
1835 // it will call ProcessRecord before next line
1836 #if !HIDEBUG_RECORD_NOT_PROCESS_VM
1837 virtualRuntime_.UpdateFromRecord(record);
1838 #endif
1839 #ifdef HIPERF_DEBUG_TIME
1840 prcessRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
1841 #endif
1842 UpdateDevHostMapsAndIPs(record);
1843 return SaveRecord(record);
1844 #endif
1845 }
1846
SaveRecord(const PerfEventRecord & record)1847 bool SubCommandRecord::SaveRecord(const PerfEventRecord& record)
1848 {
1849 #ifdef HIPERF_UNITTEST
1850 if (checkCallback_ != nullptr) {
1851 checkCallback_(record);
1852 }
1853 #endif
1854 if (fileWriter_ == nullptr) {
1855 return false;
1856 }
1857 #if HIDEBUG_RECORD_NOT_SAVE
1858 return true;
1859 #endif
1860 if (dataSizeLimit_ > 0u) {
1861 if (dataSizeLimit_ <= fileWriter_->GetDataSize()) {
1862 CHECK_TRUE(!isDataSizeLimitStop_, false, 0, "");
1863 printf("record size %" PRIu64 " is large than limit %" PRIu64 ". stop sampling.\n",
1864 fileWriter_->GetDataSize(), dataSizeLimit_);
1865 perfEvents_.StopTracking();
1866 isDataSizeLimitStop_ = true;
1867 return false;
1868 }
1869 }
1870
1871 if (record.GetName() != nullptr) {
1872 #ifdef HIPERF_DEBUG_TIME
1873 const auto saveTime = steady_clock::now();
1874 #endif
1875 if (!fileWriter_->WriteRecord(record)) {
1876 // write file failed, need stop record
1877 perfEvents_.StopTracking();
1878 HLOGV("fail to write record %s", record.GetName());
1879 return false;
1880 }
1881 if (record.GetType() == PERF_RECORD_SAMPLE) {
1882 recordSamples_++;
1883 } else {
1884 recordNoSamples_++;
1885 }
1886 HLOGV(" write done. size=%zu name=%s", record.GetSize(), record.GetName());
1887 #ifdef HIPERF_DEBUG_TIME
1888 saveRecordTimes_ += duration_cast<microseconds>(steady_clock::now() - saveTime);
1889 #endif
1890 return true;
1891 }
1892 return false;
1893 }
1894
GetCountFromFile(const std::string & fileName)1895 uint32_t SubCommandRecord::GetCountFromFile(const std::string &fileName)
1896 {
1897 uint32_t ret = 0;
1898 std::string str = ReadFileToString(fileName);
1899 std::vector<std::string> subStrs = StringSplit(str);
1900 for (auto subStr : subStrs) {
1901 ret++;
1902 std::vector<std::string> vSubstr = StringSplit(subStr, "-");
1903 static const size_t BEGIN_END = 2;
1904 if (vSubstr.size() != BEGIN_END) {
1905 continue;
1906 }
1907 int num1 = 0;
1908 int num2 = 0;
1909 if (vSubstr[1].size() >= 2 && StringEndsWith(vSubstr[1], "\n")) { // 2: string size
1910 vSubstr[1].resize(vSubstr[1].size() - 1);
1911 }
1912 bool ret1 = IsStringToIntSuccess(vSubstr[1], num1);
1913 bool ret2 = IsStringToIntSuccess(vSubstr[0], num2);
1914 if (ret1 && ret2) {
1915 ret += static_cast<uint32_t>(num1 - num2);
1916 }
1917 }
1918 return ret;
1919 }
1920
GetCpuDescFromFile()1921 std::string SubCommandRecord::GetCpuDescFromFile()
1922 {
1923 std::string str = ReadFileToString("/proc/cpuinfo");
1924 std::vector<std::string> subStrs = StringSplit(str, "\n");
1925 for (auto subStr : subStrs) {
1926 if (subStr.find("model name") == std::string::npos) {
1927 continue;
1928 }
1929
1930 std::vector<std::string> vSubstr = StringSplit(subStr, ": ");
1931 static const size_t NAME_VALUE = 2;
1932 if (vSubstr.size() == NAME_VALUE) {
1933 return vSubstr[1];
1934 } else {
1935 return "";
1936 }
1937 }
1938 return "";
1939 }
1940
AddCpuFeature()1941 bool SubCommandRecord::AddCpuFeature()
1942 {
1943 utsname unameBuf;
1944 if ((uname(&unameBuf)) != 0) {
1945 perror("uname() failed");
1946 return false;
1947 }
1948
1949 fileWriter_->AddStringFeature(FEATURE::OSRELEASE, unameBuf.release);
1950 fileWriter_->AddStringFeature(FEATURE::HOSTNAME, unameBuf.nodename);
1951 fileWriter_->AddStringFeature(FEATURE::ARCH, unameBuf.machine);
1952
1953 try {
1954 uint32_t cpuPresent = GetCountFromFile("/sys/devices/system/cpu/present");
1955 uint32_t cpuOnline = GetCountFromFile("/sys/devices/system/cpu/online");
1956 HLOGD("cpuPresent: %d, cpuOnline: %d", static_cast<int>(cpuPresent), static_cast<int>(cpuOnline));
1957 fileWriter_->AddNrCpusFeature(FEATURE::NRCPUS, cpuPresent - cpuOnline, cpuOnline);
1958 } catch (...) {
1959 HLOGD("get NRCPUS failed");
1960 return false;
1961 }
1962 std::string cpuDesc = GetCpuDescFromFile();
1963 if (!fileWriter_->AddStringFeature(FEATURE::CPUDESC, cpuDesc)) {
1964 return false;
1965 }
1966
1967 // CPUID(vendor,family,model,stepping in /proc/cpuinfo) isn't supported on Hi3516DV300
1968 // CPU_TOPOLOGY(sockets,dies,threads), isn't supported on Hi3516DV300
1969 // NUMA_TOPOLOGY
1970 // HEADER_PMU_MAPPINGS(/sys/bus/event_source/devices/cpu/type) isn't supported on Hi3516DV300
1971
1972 return true;
1973 }
1974
AddMemTotalFeature()1975 void SubCommandRecord::AddMemTotalFeature()
1976 {
1977 std::string str = ReadFileToString("/proc/meminfo");
1978 std::vector<std::string> subStrs = StringSplit(str, " ");
1979 for (auto it = subStrs.begin(); it != subStrs.end(); it++) {
1980 if (it->find("MemTotal:") == std::string::npos) {
1981 continue;
1982 }
1983
1984 if ((it + 1) != subStrs.end()) {
1985 uint64_t memTotal = 0;
1986 if (!StringToUint64(*(it + 1), memTotal)) {
1987 HIPERF_HILOGE(MODULE_DEFAULT, "get uint64_t failed, paramValue: %{public}s", (*(it + 1)).c_str());
1988 continue;
1989 }
1990 fileWriter_->AddU64Feature(FEATURE::TOTAL_MEM, memTotal);
1991 }
1992 break;
1993 }
1994 }
1995
AddEventDescFeature()1996 void SubCommandRecord::AddEventDescFeature()
1997 {
1998 fileWriter_->AddEventDescFeature(FEATURE::EVENT_DESC, perfEvents_.GetAttrWithId());
1999 }
2000
AddRecordTimeFeature()2001 void SubCommandRecord::AddRecordTimeFeature()
2002 {
2003 // create time
2004 std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
2005 // clang-format off
2006 char buf[256] = { 0 };
2007 ctime_r(&time, buf);
2008 fileWriter_->AddStringFeature(FEATURE::HIPERF_RECORD_TIME,
2009 StringReplace(buf, "\n", ""));
2010 // clang-format on
2011 return;
2012 }
2013
AddWorkloadCmdFeature()2014 void SubCommandRecord::AddWorkloadCmdFeature()
2015 {
2016 if (trackedCommand_.size() > 0) {
2017 fileWriter_->AddStringFeature(FEATURE::HIPERF_WORKLOAD_CMD, trackedCommand_.at(0));
2018 } else {
2019 HLOGD("no trackedCommand");
2020 }
2021 }
2022
AddCommandLineFeature()2023 void SubCommandRecord::AddCommandLineFeature()
2024 {
2025 // cmdline may end with some no ascii code
2026 // so we cp it with c_str again
2027 std::string fullCommandline =
2028 ReadFileToString("/proc/self/cmdline").c_str() + Command::fullArgument;
2029 fileWriter_->AddStringFeature(FEATURE::CMDLINE, fullCommandline);
2030 }
2031
AddCpuOffFeature()2032 void SubCommandRecord::AddCpuOffFeature()
2033 {
2034 if (offCPU_) {
2035 fileWriter_->AddBoolFeature(FEATURE::HIPERF_CPU_OFF);
2036 }
2037 }
2038
AddDevhostFeature()2039 void SubCommandRecord::AddDevhostFeature()
2040 {
2041 if (isHM_) {
2042 fileWriter_->AddStringFeature(FEATURE::HIPERF_HM_DEVHOST,
2043 StringPrintf("%d", virtualRuntime_.devhostPid_));
2044 }
2045 }
2046
AddFeatureRecordFile()2047 bool SubCommandRecord::AddFeatureRecordFile()
2048 {
2049 // VERSION
2050 CHECK_TRUE(AddCpuFeature(), false, 0, "");
2051 AddMemTotalFeature();
2052
2053 AddCommandLineFeature();
2054
2055 AddEventDescFeature();
2056
2057 AddRecordTimeFeature();
2058
2059 AddWorkloadCmdFeature();
2060
2061 AddCpuOffFeature();
2062
2063 AddDevhostFeature();
2064
2065 return true;
2066 }
2067
CreateInitRecordFile(const bool compressData)2068 bool SubCommandRecord::CreateInitRecordFile(const bool compressData)
2069 {
2070 if (fileWriter_ == nullptr) {
2071 fileWriter_ = std::make_unique<PerfFileWriter>();
2072 }
2073
2074 if (!fileWriter_->Open(outputFilename_, compressData)) {
2075 return false;
2076 }
2077
2078 CHECK_TRUE(fileWriter_->WriteAttrAndId(perfEvents_.GetAttrWithId(), isSpe_), false, 0, "");
2079
2080 CHECK_TRUE(AddFeatureRecordFile(), false, 0, "");
2081
2082 HLOGD("create new record file %s", outputFilename_.c_str());
2083 return true;
2084 }
2085
PostProcessRecordFile()2086 bool SubCommandRecord::PostProcessRecordFile()
2087 {
2088 if (delayUnwind_) {
2089 // 1. prepare file to rewrite
2090 std::string tempFileName = outputFilename_ + ".tmp";
2091 if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) {
2092 HLOGE("rename failed. unabel to do delay unwind");
2093 perror("Fail to rename data file");
2094 return false;
2095 } else {
2096 HLOGD("use temp file '%s' for delay unwind", tempFileName.c_str());
2097 }
2098
2099 // renew record file
2100 // release the old one
2101 fileWriter_.reset();
2102 if (!CreateInitRecordFile(compressData_)) {
2103 // create again
2104 HLOGEP("Fail to open data file %s ", outputFilename_.c_str());
2105 return false;
2106 }
2107
2108 // read temp file
2109 auto fileReader = PerfFileReader::Instance(tempFileName);
2110 if (fileReader == nullptr) {
2111 HLOGEP("Fail to open data file %s ", tempFileName.c_str());
2112 return false;
2113 }
2114
2115 // 2. read out the file and unwind
2116 auto record_callback = [&](PerfEventRecord& record) {
2117 if (record.GetName() == nullptr) {
2118 // return false in callback can stop the read process
2119 return false;
2120 } else if (record.GetType() == PERF_RECORD_SAMPLE) {
2121 HLOGM("readback record for unwind");
2122 virtualRuntime_.UnwindFromRecord(static_cast<PerfRecordSample&>(record));
2123 }
2124 SaveRecord(record);
2125 return true;
2126 };
2127 fileReader->ReadDataSection(record_callback);
2128
2129 // 3. close again
2130
2131 // lte FinishWriteRecordFile write matched only symbols
2132 delayUnwind_ = false;
2133 CHECK_TRUE(FinishWriteRecordFile(), false, 1, "Fail to finish record file %s", outputFilename_.c_str());
2134
2135 remove(tempFileName.c_str());
2136 }
2137 return true;
2138 }
2139
2140 #if USE_COLLECT_SYMBOLIC
SymbolicHits()2141 void SubCommandRecord::SymbolicHits()
2142 {
2143 if (isHM_) {
2144 for (auto &processPair : kernelThreadSymbolsHits_) {
2145 for (auto &vaddr : processPair.second) {
2146 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first,
2147 PERF_CONTEXT_MAX);
2148 }
2149 }
2150 }
2151
2152 for (auto &vaddr : kernelSymbolsHits_) {
2153 virtualRuntime_.GetSymbol(vaddr, 0, 0, PERF_CONTEXT_KERNEL);
2154 }
2155
2156 for (auto &processPair : userSymbolsHits_) {
2157 for (auto &vaddr : processPair.second) {
2158 virtualRuntime_.GetSymbol(vaddr, processPair.first, processPair.first,
2159 PERF_CONTEXT_USER);
2160 }
2161 }
2162 }
2163 #endif
2164
CollectionSymbol(PerfEventRecord & record)2165 bool SubCommandRecord::CollectionSymbol(PerfEventRecord& record)
2166 {
2167 CHECK_TRUE(record.GetName() != nullptr, false, 0, "");
2168 if (record.GetType() == PERF_RECORD_SAMPLE) {
2169 PerfRecordSample* sample = static_cast<PerfRecordSample*>(&record);
2170 #if USE_COLLECT_SYMBOLIC
2171 CollectSymbol(sample);
2172 #else
2173 virtualRuntime_.SymbolicRecord(*sample);
2174 #endif
2175 }
2176
2177 if (isSpe_ && record.GetType() == PERF_RECORD_AUXTRACE) {
2178 PerfRecordAuxtrace* sample = static_cast<PerfRecordAuxtrace*>(&record);
2179 virtualRuntime_.SymbolSpeRecord(*sample);
2180 }
2181
2182 return true;
2183 }
2184
CollectSymbol(PerfRecordSample * sample)2185 void SubCommandRecord::CollectSymbol(PerfRecordSample *sample)
2186 {
2187 CHECK_TRUE(sample != nullptr, NO_RETVAL, 0, "");
2188 perf_callchain_context context = sample->InKernel() ? PERF_CONTEXT_KERNEL
2189 : PERF_CONTEXT_USER;
2190 pid_t serverPid;
2191 // if no nr use ip ? remove stack nr == 0?
2192 if (sample->data_.nr == 0) {
2193 serverPid = sample->GetServerPidof(0);
2194 if (virtualRuntime_.IsKernelThread(serverPid)) {
2195 kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ip);
2196 } else if (context == PERF_CONTEXT_KERNEL) {
2197 kernelSymbolsHits_.insert(sample->data_.ip);
2198 } else {
2199 userSymbolsHits_[sample->data_.pid].insert(sample->data_.ip);
2200 }
2201 return;
2202 }
2203
2204 for (u64 i = 0; i < sample->data_.nr; i++) {
2205 if (sample->data_.ips[i] >= PERF_CONTEXT_MAX) {
2206 if (sample->data_.ips[i] == PERF_CONTEXT_KERNEL) {
2207 context = PERF_CONTEXT_KERNEL;
2208 } else {
2209 context = PERF_CONTEXT_USER;
2210 }
2211 continue;
2212 }
2213
2214 serverPid = sample->GetServerPidof(i);
2215 if (!isRoot_ && static_cast<uint32_t>(serverPid) == devhostPid_) {
2216 // in func UpdateDevHostCallChains add offset_ to ips, need sub offset_ when symboling
2217 if (sample->data_.ips[i] > offset_) {
2218 sample->data_.ips[i] -= offset_;
2219 }
2220 }
2221 if (virtualRuntime_.IsKernelThread(serverPid)) {
2222 kernelThreadSymbolsHits_[serverPid].insert(sample->data_.ips[i]);
2223 } else if (context == PERF_CONTEXT_KERNEL) {
2224 kernelSymbolsHits_.insert(sample->data_.ips[i]);
2225 } else {
2226 userSymbolsHits_[sample->data_.pid].insert(sample->data_.ips[i]);
2227 }
2228 }
2229 }
2230
2231 // finish writing data file, then close file
FinishWriteRecordFile()2232 bool SubCommandRecord::FinishWriteRecordFile()
2233 {
2234 #ifdef HIPERF_DEBUG_TIME
2235 const auto startTime = steady_clock::now();
2236 #endif
2237 #if !HIDEBUG_SKIP_PROCESS_SYMBOLS
2238 if (!delayUnwind_) {
2239 #if !HIDEBUG_SKIP_LOAD_KERNEL_SYMBOLS
2240 if (!callChainUserOnly_) {
2241 virtualRuntime_.UpdateKernelSymbols();
2242 virtualRuntime_.UpdateKernelModulesSymbols();
2243 if (isHM_) {
2244 virtualRuntime_.UpdateServiceSymbols();
2245 }
2246 }
2247 if (isHM_) {
2248 virtualRuntime_.UpdateDevhostSymbols();
2249 }
2250 #endif
2251 HLOGD("Load user symbols");
2252 if (dedupStack_) {
2253 virtualRuntime_.CollectDedupSymbol(kernelSymbolsHits_, userSymbolsHits_);
2254 } else {
2255 fileWriter_->ReadDataSection(
2256 [this] (PerfEventRecord& record) -> bool {
2257 return this->CollectionSymbol(record);
2258 });
2259 }
2260 #if USE_COLLECT_SYMBOLIC
2261 SymbolicHits();
2262 #endif
2263 #if HIDEBUG_SKIP_MATCH_SYMBOLS
2264 disableUnwind_ = true;
2265 #endif
2266 #if !HIDEBUG_SKIP_SAVE_SYMBOLS
2267 CHECK_TRUE(fileWriter_->AddSymbolsFeature(virtualRuntime_.GetSymbolsFiles()),
2268 false, 1, "Fail to AddSymbolsFeature");
2269 #endif
2270 }
2271 #endif
2272 CHECK_TRUE(!dedupStack_ || fileWriter_->AddUniStackTableFeature(virtualRuntime_.GetUniStackTable()), false, 0, "");
2273
2274 if (backtrack_) {
2275 virtualRuntime_.ClearSymbolCache();
2276 #if USE_COLLECT_SYMBOLIC
2277 kernelThreadSymbolsHits_.clear();
2278 kernelSymbolsHits_.clear();
2279 userSymbolsHits_.clear();
2280 #endif
2281 }
2282
2283 CHECK_TRUE(fileWriter_->Close(), false, 1, "Fail to close record file %s", outputFilename_.c_str());
2284 #ifdef HIPERF_DEBUG_TIME
2285 saveFeatureTimes_ += duration_cast<microseconds>(steady_clock::now() - startTime);
2286 #endif
2287 return true;
2288 }
2289
2290 #ifdef HIPERF_DEBUG_TIME
ReportTime()2291 void SubCommandRecord::ReportTime()
2292 {
2293 printf("updateSymbolsTimes: %0.3f ms\n",
2294 virtualRuntime_.updateSymbolsTimes_.count() / MS_DURATION);
2295 printf("saveFeatureTimes: %0.3f ms\n", saveFeatureTimes_.count() / MS_DURATION);
2296
2297 printf("prcessRecordTimes: %0.3f ms\n", prcessRecordTimes_.count() / MS_DURATION);
2298 printf("-prcessSampleRecordTimes: %0.3f ms\n",
2299 virtualRuntime_.processSampleRecordTimes_.count() / MS_DURATION);
2300 printf("--unwindFromRecordTimes: %0.3f ms\n",
2301 virtualRuntime_.unwindFromRecordTimes_.count() / MS_DURATION);
2302 printf("-prcessMmapRecordTimes: %0.3f ms\n",
2303 virtualRuntime_.processMmapRecordTimes_.count() / MS_DURATION);
2304 printf("-prcessMmap2RecordTimes: %0.3f ms\n",
2305 virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION);
2306 printf("-prcessCommRecordTimes: %0.3f ms\n",
2307 virtualRuntime_.processCommRecordTimes_.count() / MS_DURATION);
2308 printf("-prcessMmap2RecordTimes: %0.3f ms\n",
2309 virtualRuntime_.processMmap2RecordTimes_.count() / MS_DURATION);
2310 printf("--updateThreadTimes: %0.3f ms\n",
2311 virtualRuntime_.updateThreadTimes_.count() / MS_DURATION);
2312 printf("---threadParseMapsTimes: %0.3f ms\n",
2313 virtualRuntime_.threadParseMapsTimes_.count() / MS_DURATION);
2314 printf("---threadCreateMmapTimes: %0.3f ms\n",
2315 virtualRuntime_.threadCreateMmapTimes_.count() / MS_DURATION);
2316 printf("--unwindCallStackTimes: %0.3f ms\n",
2317 virtualRuntime_.unwindCallStackTimes_.count() / MS_DURATION);
2318 printf("-symbolicRecordTimes: %0.3f ms\n",
2319 virtualRuntime_.symbolicRecordTimes_.count() / MS_DURATION);
2320 printf("saveRecordTimes: %0.3f ms\n", saveRecordTimes_.count() / MS_DURATION);
2321 printf("-writeTimes: %0.3f ms\n", fileWriter_->writeTimes_.count() / MS_DURATION);
2322
2323 printf("logTimes: %0.3f ms\n", DebugLogger::GetInstance()->logTimes_.count() / MS_DURATION);
2324 printf("-logSprintfTimes: %0.3f ms\n",
2325 DebugLogger::GetInstance()->logSprintfTimes_.count() / MS_DURATION);
2326 printf("-logWriteTimes: %0.3f ms\n",
2327 DebugLogger::GetInstance()->logWriteTimes_.count() / MS_DURATION);
2328 printf("logCount: %zu (%4.2f ms/log)\n", DebugLogger::GetInstance()->logCount_,
2329 DebugLogger::GetInstance()->logTimes_.count() /
2330 static_cast<double>(DebugLogger::GetInstance()->logCount_) / MS_DURATION);
2331 }
2332 #endif
2333
RecordCompleted()2334 bool SubCommandRecord::RecordCompleted()
2335 {
2336 if (verboseReport_) {
2337 printf("Save Record used %0.3f ms.\n",
2338 duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() /
2339 MS_DURATION);
2340 }
2341 HLOGV("Save Record used %0.3f ms.\n",
2342 duration_cast<microseconds>(steady_clock::now() - startSaveFileTimes_).count() /
2343 MS_DURATION);
2344
2345 // print brief file info
2346 double mb = static_cast<double>(fileWriter_->GetDataSize()) / (KILO * KILO);
2347 if (compressData_) {
2348 printf("[ hiperf record: Captured and compressed %.3f MB perf data. ]\n", mb);
2349 } else {
2350 printf("[ hiperf record: Captured %.3f MB perf data. ]\n", mb);
2351 }
2352 printf("[ Sample records: %zu, Non sample records: %zu ]\n", recordSamples_, recordNoSamples_);
2353 // Show brief sample lost.
2354 size_t lostSamples = 0;
2355 size_t lostNonSamples = 0;
2356 perfEvents_.GetLostSamples(lostSamples, lostNonSamples);
2357 printf("[ Sample lost: %zu, Non sample lost: %zu ]\n", lostSamples, lostNonSamples);
2358
2359 #ifdef HIPERF_DEBUG_TIME
2360 ReportTime();
2361 #endif
2362 return true;
2363 }
2364
RegisterSubCommandRecord(void)2365 bool SubCommandRecord::RegisterSubCommandRecord(void)
2366 {
2367 return SubCommand::RegisterSubCommand("record", SubCommandRecord::GetInstance);
2368 }
2369
SetHM()2370 void SubCommandRecord::SetHM()
2371 {
2372 isHM_ = IsHM();
2373 virtualRuntime_.SetHM(isHM_);
2374 perfEvents_.SetHM(isHM_);
2375 HLOGD("Set isHM_: %d", isHM_);
2376 if (isHM_) {
2377 // find devhost pid
2378 const std::string basePath {"/proc/"};
2379 std::vector<std::string> subDirs = GetSubDirs(basePath);
2380 for (const auto &subDir : subDirs) {
2381 if (!IsDigits(subDir)) {
2382 continue;
2383 }
2384 pid_t pid = std::stoll(subDir);
2385 std::string cmdline = GetProcessName(pid);
2386 if (cmdline == "/bin/" + DEVHOST_FILE_NAME) {
2387 virtualRuntime_.SetDevhostPid(pid);
2388 devhostPid_ = static_cast<uint32_t>(pid);
2389 break;
2390 }
2391 }
2392 }
2393 }
2394
OnlineReportData()2395 bool SubCommandRecord::OnlineReportData()
2396 {
2397 if (!report_) {
2398 return true;
2399 }
2400 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s begin to report file",
2401 __FUNCTION__);
2402 bool ret = false;
2403 std::string tempFileName = outputFilename_ + ".tmp";
2404 if (rename(outputFilename_.c_str(), tempFileName.c_str()) != 0) {
2405 char errInfo[ERRINFOLEN] = { 0 };
2406 strerror_r(errno, errInfo, ERRINFOLEN);
2407 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s can't rename file "
2408 "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n",
2409 __FUNCTION__, errno, errInfo);
2410 return false;
2411 }
2412
2413 std::unique_ptr<SubCommandReport> reporter = std::make_unique<SubCommandReport>();
2414 HLOGD("report the file %s to report file %s \n", tempFileName.c_str(), outputFilename_.c_str());
2415 std::vector<std::string> args;
2416 args.emplace_back("-i");
2417 args.emplace_back(tempFileName);
2418 args.emplace_back("-o");
2419 args.emplace_back(outputFilename_);
2420 args.emplace_back("-s");
2421 if (reporter->ParseOption(args)) {
2422 ret = (reporter->OnSubCommand(args) != HiperfError::NO_ERR);
2423 }
2424
2425 if (remove(tempFileName.c_str()) != 0) {
2426 char errInfo[ERRINFOLEN] = { 0 };
2427 strerror_r(errno, errInfo, ERRINFOLEN);
2428 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s remove file failed"
2429 "errno:%" HILOG_PUBLIC "d , errInfo: %" HILOG_PUBLIC "s\n",
2430 __FUNCTION__, errno, errInfo);
2431 }
2432 HIPERF_HILOGI(MODULE_DEFAULT, "%" HILOG_PUBLIC "s report result %" HILOG_PUBLIC "s",
2433 __FUNCTION__, ret ? "success" : "fail");
2434 return ret;
2435 }
2436
AddReportArgs(CommandReporter & reporter)2437 void SubCommandRecord::AddReportArgs(CommandReporter& reporter)
2438 {
2439 if (targetSystemWide_) {
2440 reporter.targetProcess_ = "ALL";
2441 } else if (!appPackage_.empty()) {
2442 reporter.targetProcess_ = appPackage_;
2443 } else {
2444 std::unordered_set<std::string> processNames = {};
2445 for_each(selectPids_.begin(), selectPids_.end(), [&processNames] (const pid_t& pid) {
2446 processNames.insert(GetProcessName(pid));
2447 });
2448 reporter.targetProcess_ = SetToString<std::string>(processNames);
2449 }
2450 }
2451
CollectExcludeThread()2452 void SubCommandRecord::CollectExcludeThread()
2453 {
2454 if (!excludeProcessNameArgs_.empty()) {
2455 CollectPidsByAppname(excludePids_, excludeProcessNameArgs_);
2456 }
2457 excludeTids_.insert(excludeTidArgs_.begin(), excludeTidArgs_.end());
2458 }
2459
SetExcludeHiperf()2460 void SubCommandRecord::SetExcludeHiperf()
2461 {
2462 if (excludeHiperf_) {
2463 excludePids_.emplace(getpid());
2464 }
2465 }
2466
IsThreadExcluded(const pid_t pid,const pid_t tid)2467 bool SubCommandRecord::IsThreadExcluded(const pid_t pid, const pid_t tid)
2468 {
2469 if (excludePids_.find(pid) != excludePids_.end()) {
2470 return true;
2471 }
2472 if (excludeTids_.find(tid) != excludeTids_.end()) {
2473 return true;
2474 }
2475 return false;
2476 }
2477
GetInstance()2478 SubCommand& SubCommandRecord::GetInstance()
2479 {
2480 static SubCommandRecord subCommand;
2481 return subCommand;
2482 }
2483
SetCheckRecordCallback(CheckRecordCallBack callback)2484 void SubCommandRecord::SetCheckRecordCallback(CheckRecordCallBack callback)
2485 {
2486 #ifdef HIPERF_UNITTEST
2487 checkCallback_ = callback;
2488 #endif
2489 }
2490
GetOffsetNum()2491 uint32_t SubCommandRecord::GetOffsetNum()
2492 {
2493 uint32_t result = 0;
2494 std::random_device rd;
2495 std::mt19937 gen(rd());
2496 std::uniform_int_distribution<uint32_t> dis(0, UINT32_MAX);
2497 result = dis(gen);
2498
2499 std::ifstream randomFile("/dev/random", std::ios::binary);
2500 do {
2501 if (!randomFile.is_open()) {
2502 break;
2503 }
2504 randomFile.read(reinterpret_cast<char*>(&result), sizeof(result));
2505 } while (0);
2506 randomFile.close();
2507 return result;
2508 }
2509
UpdateDevHostMaps(PerfEventRecord & record)2510 void SubCommandRecord::UpdateDevHostMaps(PerfEventRecord& record)
2511 {
2512 if (record.GetType() == PERF_RECORD_MMAP) {
2513 auto recordMmap = static_cast<PerfRecordMmap*>(&record);
2514 if (recordMmap->data_.pid == devhostPid_) {
2515 recordMmap->data_.addr += offset_;
2516 }
2517 } else if (record.GetType() == PERF_RECORD_MMAP2) {
2518 auto recordMmap2 = static_cast<PerfRecordMmap2*>(&record);
2519 if (recordMmap2->data_.pid == devhostPid_) {
2520 recordMmap2->data_.addr += offset_;
2521 }
2522 }
2523 }
2524
UpdateDevHostCallChains(PerfEventRecord & record)2525 void SubCommandRecord::UpdateDevHostCallChains(PerfEventRecord& record)
2526 {
2527 if (record.GetType() == PERF_RECORD_SAMPLE) {
2528 uint32_t serverPid;
2529 const uint64_t BAD_IP_ADDRESS = 2;
2530 auto sample = static_cast<PerfRecordSample*>(&record);
2531 serverPid = static_cast<uint32_t>(sample->GetServerPidof(0));
2532 if (serverPid == devhostPid_) {
2533 sample->data_.ip += offset_;
2534 }
2535 for (u64 i = 0; i < sample->data_.nr; i++) {
2536 if (sample->data_.ips[i] >= PERF_CONTEXT_MAX || sample->data_.ips[i] < BAD_IP_ADDRESS) {
2537 continue;
2538 }
2539 serverPid = static_cast<uint32_t>(sample->GetServerPidof(i));
2540 if (serverPid == devhostPid_) {
2541 sample->data_.ips[i] += offset_;
2542 }
2543 }
2544 }
2545 }
2546 } // namespace HiPerf
2547 } // namespace Developtools
2548 } // namespace OHOS
2549