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