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