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