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