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 #include "perf_events.h"
16
17 #include <cassert>
18 #include <cinttypes>
19 #include <csignal>
20 #include <cstdint>
21 #include <cstdlib>
22 #include <iostream>
23 #include <sys/ioctl.h>
24 #include <sys/mman.h>
25 #include <sys/resource.h>
26 #include <sys/syscall.h>
27 #include <unistd.h>
28 #if defined(CONFIG_HAS_SYSPARA)
29 #include <parameters.h>
30 #endif
31
32 #include "debug_logger.h"
33 #include "register.h"
34 #include "subcommand_dump.h"
35 #include "symbols_file.h"
36 #include "utilities.h"
37
38 using namespace std;
39 using namespace std::chrono;
40 namespace OHOS {
41 namespace Developtools {
42 namespace HiPerf {
43 static std::atomic_bool g_trackRunning = false;
44
Open(perf_event_attr & attr,pid_t pid,int cpu,int group_fd,unsigned long flags)45 OHOS::UniqueFd PerfEvents::Open(perf_event_attr &attr, pid_t pid, int cpu, int group_fd,
46 unsigned long flags)
47 {
48 OHOS::UniqueFd fd = UniqueFd(syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, flags));
49 if (fd < 0) {
50 HLOGEP("syscall perf_event_open failed. ");
51 // dump when open failed.
52 SubCommandDump::DumpPrintEventAttr(attr, std::numeric_limits<int>::min());
53 }
54 HLOGV("perf_event_open: got fd %d for pid %d cpu %d group %d flags %lu", fd.Get(), pid, cpu, group_fd, flags);
55 return fd;
56 }
57
PerfEvents()58 PerfEvents::PerfEvents() : timeOut_(DEFAULT_TIMEOUT * THOUSANDS), timeReport_(0)
59 {
60 pageSize_ = sysconf(_SC_PAGESIZE);
61 HLOGI("BuildArch %s", GetArchName(buildArchType).c_str());
62 }
63
~PerfEvents()64 PerfEvents::~PerfEvents()
65 {
66 // close mmap
67 for (auto it = cpuMmap_.begin(); it != cpuMmap_.end();) {
68 const MmapFd &mmapItem = it->second;
69 munmap(mmapItem.mmapPage, (1 + mmapPages_) * pageSize_);
70 it = cpuMmap_.erase(it);
71 }
72
73 // close file descriptor of perf_event_open() created
74 for (auto eventGroupItem = eventGroupItem_.begin(); eventGroupItem != eventGroupItem_.end();) {
75 for (const auto &eventItem : eventGroupItem->eventItems) {
76 for (const auto &fdItem : eventItem.fdItems) {
77 close(fdItem.fd);
78 }
79 }
80 eventGroupItem = eventGroupItem_.erase(eventGroupItem);
81 }
82
83 ExitReadRecordBufThread();
84 }
85
IsEventSupport(perf_type_id type,__u64 config)86 bool PerfEvents::IsEventSupport(perf_type_id type, __u64 config)
87 {
88 HLOGV("enter");
89 unique_ptr<perf_event_attr> attr = PerfEvents::CreateDefaultAttr(type, config);
90 UniqueFd fd = Open(*attr.get());
91 if (fd < 0) {
92 printf("event not support %s\n", GetStaticConfigName(type, config).c_str());
93 return false;
94 } else {
95 return true;
96 }
97 }
IsEventAttrSupport(perf_event_attr & attr)98 bool PerfEvents::IsEventAttrSupport(perf_event_attr &attr)
99 {
100 HLOGV("enter");
101 UniqueFd fd = Open(attr);
102 if (fd < 0) {
103 return false;
104 } else {
105 return true;
106 }
107 }
108
SetBranchSampleType(uint64_t value)109 bool PerfEvents::SetBranchSampleType(uint64_t value)
110 {
111 if (value != 0) {
112 // cpu-clcles event must be supported
113 unique_ptr<perf_event_attr> attr =
114 PerfEvents::CreateDefaultAttr(PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
115 attr->sample_type |= PERF_SAMPLE_BRANCH_STACK;
116 attr->branch_sample_type = value;
117 if (!IsEventAttrSupport(*attr.get())) {
118 return false;
119 }
120 }
121 branchSampleType_ = value;
122 return true;
123 }
124
AddDefaultEvent(perf_type_id type)125 bool PerfEvents::AddDefaultEvent(perf_type_id type)
126 {
127 HLOGV("enter");
128 auto it = DEFAULT_TYPE_CONFIGS.find(type);
129 if (it != DEFAULT_TYPE_CONFIGS.end()) {
130 for (auto config : it->second) {
131 AddEvent(type, config);
132 }
133 }
134 return true;
135 }
136
AddOffCpuEvent()137 bool PerfEvents::AddOffCpuEvent()
138 {
139 std::string eventName = "sched:sched_switch";
140 if (eventSpaceType_ == EventSpaceType::USER) {
141 eventName += ":u";
142 } else if (eventSpaceType_ == EventSpaceType::KERNEL) {
143 eventName += ":k";
144 }
145 return AddEvent(eventName);
146 }
147
AddEvents(const std::vector<std::string> & eventStrings,bool group)148 bool PerfEvents::AddEvents(const std::vector<std::string> &eventStrings, bool group)
149 {
150 bool followGroup = false;
151 HLOGV(" %s %s", VectorToString(eventStrings).c_str(), followGroup ? "followGroup" : "");
152
153 for (std::string eventString : eventStrings) {
154 if (!AddEvent(eventString, followGroup)) {
155 return false;
156 }
157 // this is group request , Follow-up events need to follow the previous group
158 if (group) {
159 followGroup = true;
160 }
161 }
162 return true;
163 }
164
165 // event name can have :k or :u suffix
166 // tracepoint event name is like sched:sched_switch
167 // clang-format off
ParseEventName(const std::string & nameStr,std::string & name,bool & excludeUser,bool & excludeKernel,bool & isTracePoint)168 bool PerfEvents::ParseEventName(const std::string &nameStr,
169 std::string &name, bool &excludeUser, bool &excludeKernel, bool &isTracePoint)
170 // clang-format on
171 {
172 name = nameStr;
173 excludeUser = false;
174 excludeKernel = false;
175 isTracePoint = false;
176 if (nameStr.find(":") != std::string::npos) {
177 static constexpr size_t maxNumberTokensNoTracePoint = 2;
178 static constexpr size_t maxNumberTokensTracePoint = 3;
179 std::vector<std::string> eventTokens = StringSplit(nameStr, ":");
180 if (eventTokens.size() == maxNumberTokensTracePoint) {
181 // tracepoint event with :u or :k
182 if (eventTokens.back() == "k") {
183 excludeUser = true;
184 HLOGV("kernelOnly event");
185 } else if (eventTokens.back() == "u") {
186 excludeKernel = true;
187 HLOGV("userOnly event");
188 } else {
189 HLOGV("unknown event name %s", nameStr.c_str());
190 return false;
191 }
192 name = eventTokens[0] + ":" + eventTokens[1];
193 isTracePoint = true;
194 } else if (eventTokens.size() == maxNumberTokensNoTracePoint) {
195 name = eventTokens[0];
196 if (eventTokens.back() == "k") {
197 excludeUser = true;
198 HLOGV("kernelOnly event");
199 } else if (eventTokens.back() == "u") {
200 excludeKernel = true;
201 HLOGV("userOnly event");
202 } else {
203 name = nameStr;
204 isTracePoint = true;
205 HLOGV("tracepoint event is in form of xx:xxx");
206 }
207 } else {
208 printf("unknown ':' format:'%s'\n", nameStr.c_str());
209 return false;
210 }
211 if (reportCallBack_) {
212 if ((eventTokens[0] == "sw-task-clock" || eventTokens[0] == "sw-cpu-clock") &&
213 (excludeUser || excludeKernel)) {
214 printf(
215 "event type %s with modifier u and modifier k is not supported by the kernel.",
216 eventTokens[0].c_str());
217 return false;
218 }
219 }
220 }
221 return true;
222 }
223
AddEvent(const std::string & eventString,bool followGroup)224 bool PerfEvents::AddEvent(const std::string &eventString, bool followGroup)
225 {
226 std::string eventName;
227 bool excludeUser = false;
228 bool excludeKernel = false;
229 bool isTracePointEvent = false;
230 if (!ParseEventName(eventString, eventName, excludeUser, excludeKernel, isTracePointEvent)) {
231 return false;
232 }
233 if (excludeUser) {
234 eventSpaceType_ |= EventSpaceType::KERNEL;
235 } else if (excludeKernel) {
236 eventSpaceType_ |= EventSpaceType::USER;
237 } else {
238 eventSpaceType_ |= EventSpaceType::USER_KERNEL;
239 }
240
241 // find if
242 if (isTracePointEvent) {
243 if (traceConfigTable.empty()) {
244 LoadTracepointEventTypesFromSystem();
245 }
246 for (auto traceType : traceConfigTable) {
247 if (traceType.second == eventName) {
248 return AddEvent(PERF_TYPE_TRACEPOINT, traceType.first, excludeUser, excludeKernel,
249 followGroup);
250 }
251 }
252 } else {
253 for (auto type : TYPE_CONFIGS) {
254 for (auto config : (type.second)) {
255 if (config.second == eventName) {
256 return AddEvent(type.first, config.first, excludeUser, excludeKernel,
257 followGroup);
258 }
259 }
260 }
261 }
262
263 printf("%s event is not supported by the kernel.\n", eventName.c_str());
264 return false;
265 }
266
AddEvent(perf_type_id type,__u64 config,bool excludeUser,bool excludeKernel,bool followGroup)267 bool PerfEvents::AddEvent(perf_type_id type, __u64 config, bool excludeUser, bool excludeKernel,
268 bool followGroup)
269 {
270 HLOG_ASSERT(!excludeUser or !excludeKernel);
271 if (followGroup && eventGroupItem_.empty()) {
272 HLOGE("no group leader create before");
273 return false;
274 }
275 // found the event name
276 if (!IsEventSupport(type, config)) {
277 return false;
278 }
279 HLOGV("type %d config %llu excludeUser %d excludeKernel %d followGroup %d", type, config,
280 excludeUser, excludeKernel, followGroup);
281
282 // if use follow ?
283 EventGroupItem &eventGroupItem = followGroup ? eventGroupItem_.back()
284 : eventGroupItem_.emplace_back();
285 // always new item
286 EventItem &eventItem = eventGroupItem.eventItems.emplace_back();
287
288 eventItem.typeName = GetTypeName(type);
289 if (type == PERF_TYPE_TRACEPOINT) {
290 eventItem.configName = GetTraceConfigName(config);
291 } else {
292 eventItem.configName = GetStaticConfigName(type, config);
293 }
294
295 // attr
296 if (memset_s(&eventItem.attr, sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) {
297 HLOGE("memset_s failed in PerfEvents::AddEvent");
298 return false;
299 }
300 eventItem.attr.size = sizeof(perf_event_attr);
301 eventItem.attr.type = type;
302 eventItem.attr.config = config;
303 eventItem.attr.disabled = 1;
304 eventItem.attr.read_format =
305 PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
306
307 eventItem.attr.inherit = (inherit_ ? 1 : 0);
308 eventItem.attr.exclude_kernel = excludeKernel;
309 eventItem.attr.exclude_user = excludeUser;
310
311 // we also need mmap for record
312 if (recordCallBack_) {
313 if (samplePeriod_ > 0) {
314 eventItem.attr.freq = 0;
315 eventItem.attr.sample_freq = 0;
316 eventItem.attr.sample_period = samplePeriod_;
317 } else if (sampleFreq_ > 0) {
318 eventItem.attr.freq = 1;
319 eventItem.attr.sample_freq = sampleFreq_;
320 } else {
321 if (type == PERF_TYPE_TRACEPOINT) {
322 eventItem.attr.freq = 0;
323 eventItem.attr.sample_period = DEFAULT_SAMPLE_PERIOD;
324 } else {
325 eventItem.attr.freq = 1;
326 eventItem.attr.sample_freq = DEFAULT_SAMPLE_FREQUNCY;
327 }
328 }
329
330 eventItem.attr.watermark = 1;
331 if (eventItem.attr.watermark == 1) {
332 eventItem.attr.wakeup_watermark = (mmapPages_ * pageSize_) >> 1;
333 static constexpr unsigned int maxWakeupMark = 1024 * 1024;
334 if (eventItem.attr.wakeup_watermark > maxWakeupMark) {
335 eventItem.attr.wakeup_watermark = maxWakeupMark;
336 }
337 }
338
339 // for a group of events, only enable comm/mmap on the first event
340 if (!followGroup) {
341 eventItem.attr.comm = 1;
342 eventItem.attr.mmap = 1;
343 eventItem.attr.mmap2 = 1;
344 eventItem.attr.mmap_data = 0;
345 }
346
347 if (sampleStackType_ == SampleStackType::DWARF) {
348 eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN |
349 PERF_SAMPLE_STACK_USER | PERF_SAMPLE_REGS_USER;
350 eventItem.attr.exclude_callchain_user = 1;
351 eventItem.attr.sample_regs_user = GetSupportedRegMask(GetDeviceArch());
352 eventItem.attr.sample_stack_user = dwarfSampleStackSize_;
353 } else if (sampleStackType_ == SampleStackType::FP) {
354 eventItem.attr.sample_type = SAMPLE_TYPE | PERF_SAMPLE_CALLCHAIN;
355 } else {
356 eventItem.attr.sample_type = SAMPLE_TYPE;
357 }
358 }
359
360 // set clock id
361 if (clockId_ != -1) {
362 eventItem.attr.use_clockid = 1;
363 eventItem.attr.clockid = clockId_;
364 }
365 if (branchSampleType_ != 0) {
366 eventItem.attr.sample_type |= PERF_SAMPLE_BRANCH_STACK;
367 eventItem.attr.branch_sample_type = branchSampleType_;
368 }
369
370 HLOGV("Add Event: '%s':'%s' %s %s %s", eventItem.typeName.c_str(), eventItem.configName.c_str(),
371 excludeUser ? "excludeUser" : "", excludeKernel ? "excludeKernel" : "",
372 followGroup ? "" : "group leader");
373
374 return true;
375 }
376
CreateDefaultAttr(perf_type_id type,__u64 config)377 std::unique_ptr<perf_event_attr> PerfEvents::CreateDefaultAttr(perf_type_id type, __u64 config)
378 {
379 unique_ptr<perf_event_attr> attr = make_unique<perf_event_attr>();
380 if (memset_s(attr.get(), sizeof(perf_event_attr), 0, sizeof(perf_event_attr)) != EOK) {
381 HLOGE("memset_s failed in PerfEvents::CreateDefaultAttr");
382 return nullptr;
383 }
384 attr->size = sizeof(perf_event_attr);
385 attr->type = type;
386 attr->config = config;
387 attr->disabled = 1;
388 return attr;
389 }
390
391 // should move to upper caller
392 static struct sigaction g_oldSig {
393 };
CaptureSig()394 static bool CaptureSig()
395 {
396 HLOGD("capture Ctrl + C to end sampling decently");
397 struct sigaction sig {
398 };
399
400 sig.sa_handler = [](int sig) {
401 printf("\n Ctrl + C detected.\n");
402 g_trackRunning = false;
403 };
404
405 sig.sa_flags = 0;
406 if (sigaction(SIGINT, &sig, &g_oldSig) < 0) {
407 perror("Fail to call sigaction for SIGINT");
408 return false;
409 }
410 return true;
411 }
412
RecoverCaptureSig()413 static void RecoverCaptureSig()
414 {
415 if (sigaction(SIGINT, &g_oldSig, nullptr) < 0) {
416 perror("Fail to call sigaction for SIGINT");
417 }
418 }
419
420 // split to two part
421 // because WriteAttrAndId need fd id before start tracking
PrepareTracking(void)422 bool PerfEvents::PrepareTracking(void)
423 {
424 HLOGV("enter");
425 // 1. prepare cpu pid
426 if (!PrepareFdEvents()) {
427 HLOGE("PrepareFdEvents() failed");
428 return false;
429 }
430
431 // 2. create events
432 if (!CreateFdEvents()) {
433 HLOGE("CreateFdEvents() failed");
434 return false;
435 }
436
437 HLOGV("success");
438 prepared_ = true;
439 return true;
440 }
441
ExitReadRecordBufThread()442 void PerfEvents::ExitReadRecordBufThread()
443 {
444 if (isLowPriorityThread_) {
445 if (setpriority(PRIO_PROCESS, gettid(), 0) != 0) {
446 HLOGW("failed to decrease priority of reading kernel");
447 }
448 }
449 if (readRecordBufThread_.joinable()) {
450 {
451 std::lock_guard<std::mutex> lk(mtxRrecordBuf_);
452 readRecordThreadRunning_ = false;
453 __sync_synchronize();
454 cvRecordBuf_.notify_one();
455 }
456 readRecordBufThread_.join();
457 }
458 }
459
PrepareRecordThread()460 bool PerfEvents::PrepareRecordThread()
461 {
462 try {
463 recordBuf_ = std::make_unique<RingBuffer>(CalcBufferSize());
464 } catch (const std::exception &e) {
465 printf("create record buffer(size %zu) failed: %s\n", CalcBufferSize(), e.what());
466 return false;
467 }
468 readRecordThreadRunning_ = true;
469 readRecordBufThread_ = std::thread(&PerfEvents::ReadRecordFromBuf, this);
470
471 rlimit rlim;
472 int result = getrlimit(RLIMIT_NICE, &rlim);
473 const rlim_t lowPriority = 40;
474 if (result == 0 && rlim.rlim_cur == lowPriority) {
475 const int highPriority = -20;
476 result = setpriority(PRIO_PROCESS, gettid(), highPriority);
477 if (result != 0) {
478 HLOGW("failed to increase priority of reading kernel");
479 } else {
480 isLowPriorityThread_ = true;
481 }
482 }
483
484 return true;
485 }
486
WaitRecordThread()487 void PerfEvents::WaitRecordThread()
488 {
489 printf("Process and Saving data...\n");
490 ExitReadRecordBufThread();
491
492 const auto usedTimeMsTick = duration_cast<milliseconds>(steady_clock::now() - trackingEndTime_);
493 if (verboseReport_) {
494 printf("Record Process Completed (wait %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count());
495 }
496 HLOGV("Record Process Completed (wait %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count());
497 #ifdef HIPERF_DEBUG_TIME
498 printf("%zu record processed, used %0.3f ms(%4.2f us/record)\n", recordEventCount_,
499 recordCallBackTime_.count() / MS_DUARTION,
500 recordCallBackTime_.count() / static_cast<double>(recordEventCount_));
501 printf("total wait sleep time %0.3f ms.\n", recordSleepTime_.count() / MS_DUARTION);
502 printf("read from kernel time %0.3f ms.\n", recordKernelReadTime_.count() / MS_DUARTION);
503 #endif
504 }
505
StartTracking(bool immediately)506 bool PerfEvents::StartTracking(bool immediately)
507 {
508 HLOGV("enter");
509 if (!prepared_) {
510 HLOGD("do not prepared_");
511 return false;
512 }
513
514 HLOGD("step: 1. enable event");
515 trackingStartTime_ = steady_clock::now();
516 if (immediately) {
517 if (!EnableTracking()) {
518 HLOGE("PerfEvents::EnableTracking() failed");
519 return false;
520 }
521 }
522
523 if (recordCallBack_) {
524 if (!PrepareRecordThread()) {
525 return false;
526 }
527 }
528
529 if (immediately) {
530 printf("Profiling duration is %.3f seconds.\n", float(timeOut_.count()) / THOUSANDS);
531 printf("Start Profiling...\n");
532 }
533
534 g_trackRunning = true;
535 if (!CaptureSig()) {
536 HLOGE("captureSig() failed");
537 g_trackRunning = false;
538 ExitReadRecordBufThread();
539 return false;
540 }
541
542 HLOGD("step: 2. thread loop");
543 if (recordCallBack_) {
544 RecordLoop();
545 } else {
546 StatLoop();
547 }
548
549 HLOGD("step: 3. disable event");
550 if (!PerfEventsEnable(false)) {
551 HLOGE("PerfEvents::PerfEventsEnable() failed");
552 }
553 trackingEndTime_ = steady_clock::now();
554
555 RecoverCaptureSig();
556
557 if (recordCallBack_) {
558 WaitRecordThread();
559 }
560
561 HLOGD("step: 4. exit");
562 return true;
563 }
564
StopTracking(void)565 bool PerfEvents::StopTracking(void)
566 {
567 if (g_trackRunning) {
568 printf("some one called StopTracking\n");
569 g_trackRunning = false;
570 if (trackedCommand_) {
571 if (trackedCommand_->GetState() == TrackedCommand::State::COMMAND_STARTED) {
572 trackedCommand_->Stop();
573 }
574 }
575 if (!PerfEventsEnable(false)) {
576 HLOGE("StopTracking : PerfEventsEnable(false) failed");
577 return false;
578 }
579 }
580 return true;
581 }
582
PauseTracking(void)583 bool PerfEvents::PauseTracking(void)
584 {
585 if (!startedTracking_) {
586 return false;
587 }
588 return PerfEventsEnable(false);
589 }
590
ResumeTracking(void)591 bool PerfEvents::ResumeTracking(void)
592 {
593 if (!startedTracking_) {
594 return false;
595 }
596 return PerfEventsEnable(true);
597 }
598
EnableTracking()599 bool PerfEvents::EnableTracking()
600 {
601 if (startedTracking_) {
602 return true;
603 }
604 if (!PerfEventsEnable(true)) {
605 HLOGE("PerfEvents::PerfEventsEnable() failed");
606 return false;
607 }
608
609 if (trackedCommand_) {
610 // start tracked Command
611 if (trackedCommand_->GetState() == TrackedCommand::State::COMMAND_WAITING) {
612 if (!trackedCommand_->StartCommand()) {
613 int wstatus;
614 if (!trackedCommand_->WaitCommand(wstatus)) {
615 trackedCommand_->Stop();
616 }
617 std::string commandName = trackedCommand_->GetCommandName();
618 printf("failed to execute command: %zu: %s\n", commandName.size(), commandName.c_str());
619 return false;
620 }
621 } else if (trackedCommand_->GetState() != TrackedCommand::State::COMMAND_STARTED) {
622 return false;
623 }
624 }
625 startedTracking_ = true;
626 return true;
627 }
628
IsTrackRunning()629 bool PerfEvents::IsTrackRunning()
630 {
631 return g_trackRunning;
632 }
633
SetSystemTarget(bool systemTarget)634 void PerfEvents::SetSystemTarget(bool systemTarget)
635 {
636 systemTarget_ = systemTarget;
637 }
638
SetCpu(std::vector<pid_t> cpus)639 void PerfEvents::SetCpu(std::vector<pid_t> cpus)
640 {
641 cpus_ = cpus;
642 }
643
SetPid(std::vector<pid_t> pids)644 void PerfEvents::SetPid(std::vector<pid_t> pids)
645 {
646 pids_ = pids;
647 }
648
SetTimeOut(float timeOut)649 void PerfEvents::SetTimeOut(float timeOut)
650 {
651 if (timeOut > 0) {
652 timeOut_ = milliseconds(static_cast<int>(timeOut * THOUSANDS));
653 }
654 }
655
SetTimeReport(int timeReport)656 void PerfEvents::SetTimeReport(int timeReport)
657 {
658 static constexpr int minMsReportInterval = 100;
659 if (timeReport < minMsReportInterval && timeReport != 0) {
660 timeReport = minMsReportInterval;
661 printf("time report min value is %d.\n", timeReport);
662 }
663
664 timeReport_ = milliseconds(timeReport);
665 }
666
GetSupportEvents(perf_type_id type)667 std::map<__u64, std::string> PerfEvents::GetSupportEvents(perf_type_id type)
668 {
669 if (type == PERF_TYPE_TRACEPOINT) {
670 LoadTracepointEventTypesFromSystem();
671 }
672
673 std::map<__u64, std::string> eventConfigs;
674 auto configTable = TYPE_CONFIGS.find(type);
675 if (configTable != TYPE_CONFIGS.end()) {
676 auto configs = configTable->second;
677 for (auto config : configs) {
678 if (type == PERF_TYPE_TRACEPOINT || IsEventSupport(type, (__u64)config.first)) {
679 eventConfigs.insert(config);
680 } else {
681 HLOGD("'%s' not support", config.second.c_str());
682 }
683 }
684 }
685 return eventConfigs;
686 }
687
LoadTracepointEventTypesFromSystem()688 void PerfEvents::LoadTracepointEventTypesFromSystem()
689 {
690 if (traceConfigTable.empty()) {
691 std::string basePath {"/sys/kernel/tracing/events"};
692 if (access(basePath.c_str(), R_OK) != 0) {
693 basePath = "/sys/kernel/debug/tracing/events";
694 }
695 for (const auto &eventName : GetSubDirs(basePath)) {
696 std::string eventPath = basePath + "/" + eventName;
697 for (const auto &concreteEvent : GetSubDirs(eventPath)) {
698 std::string idPath = eventPath + "/" + concreteEvent + "/id";
699 {
700 std::ifstream ifs {idPath};
701 // clang-format off
702 const std::string idStr = {
703 std::istream_iterator<char>(ifs),
704 std::istream_iterator<char>()
705 };
706 // clang-format on
707 __u64 id {0};
708 try {
709 id = std::stoul(idStr, nullptr);
710 } catch (...) {
711 continue;
712 }
713 auto typeConfigs = TYPE_CONFIGS.find(PERF_TYPE_TRACEPOINT);
714 HLOG_ASSERT(typeConfigs != TYPE_CONFIGS.end());
715 auto configPair = typeConfigs->second.insert(
716 std::make_pair(id, eventName + ":" + concreteEvent));
717 traceConfigTable.insert(std::make_pair(id, eventName + ":" + concreteEvent));
718 ConfigTable::iterator it = configPair.first;
719 HLOGV("TYPE_CONFIGS add %llu:%s in %zu", it->first, it->second.c_str(),
720 typeConfigs->second.size());
721 }
722 }
723 }
724 }
725 }
726
SetVerboseReport(bool verboseReport)727 void PerfEvents::SetVerboseReport(bool verboseReport)
728 {
729 verboseReport_ = verboseReport;
730 }
731
SetSampleFrequency(unsigned int frequency)732 void PerfEvents::SetSampleFrequency(unsigned int frequency)
733 {
734 if (frequency > 0) {
735 sampleFreq_ = frequency;
736 }
737 }
738
SetSamplePeriod(unsigned int period)739 void PerfEvents::SetSamplePeriod(unsigned int period)
740 {
741 if (period > 0) {
742 samplePeriod_ = period;
743 }
744 }
745
SetMmapPages(size_t mmapPages)746 void PerfEvents::SetMmapPages(size_t mmapPages)
747 {
748 mmapPages_ = mmapPages;
749 }
750
SetSampleStackType(SampleStackType type)751 void PerfEvents::SetSampleStackType(SampleStackType type)
752 {
753 sampleStackType_ = type;
754 }
755
SetDwarfSampleStackSize(uint32_t stackSize)756 void PerfEvents::SetDwarfSampleStackSize(uint32_t stackSize)
757 {
758 HLOGD("request stack size is %u", stackSize);
759 dwarfSampleStackSize_ = stackSize;
760 }
761
PerfEventsEnable(bool enable)762 bool PerfEvents::PerfEventsEnable(bool enable)
763 {
764 HLOGV("%s", std::to_string(enable).c_str());
765 for (const auto &eventGroupItem : eventGroupItem_) {
766 for (const auto &eventItem : eventGroupItem.eventItems) {
767 for (const auto &fdItem : eventItem.fdItems) {
768 int result =
769 ioctl(fdItem.fd, enable ? PERF_EVENT_IOC_ENABLE : PERF_EVENT_IOC_DISABLE, 0);
770 if (result < 0) {
771 printf("Cannot '%s' perf fd! type config name: '%s:%s'\n",
772 enable ? "enable" : "disable", eventItem.typeName.c_str(),
773 eventItem.configName.c_str());
774 return false;
775 }
776 }
777 }
778 }
779 return true;
780 }
781
SetStatCallBack(StatCallBack reportCallBack)782 void PerfEvents::SetStatCallBack(StatCallBack reportCallBack)
783 {
784 reportCallBack_ = reportCallBack;
785 }
SetRecordCallBack(RecordCallBack recordCallBack)786 void PerfEvents::SetRecordCallBack(RecordCallBack recordCallBack)
787 {
788 recordCallBack_ = recordCallBack;
789 }
790
PutAllCpus()791 inline void PerfEvents::PutAllCpus()
792 {
793 int processorNum = GetProcessorNum();
794 for (int i = 0; i < processorNum; i++) {
795 cpus_.push_back(i); // put all cpu
796 }
797 }
798
PrepareFdEvents(void)799 bool PerfEvents::PrepareFdEvents(void)
800 {
801 HLOGV("enter");
802 /*
803 https://man7.org/linux/man-pages/man2/perf_event_open.2.html
804 pid == 0 and cpu == -1
805 This measures the calling process/thread on any CPU.
806
807 pid == 0 and cpu >= 0
808 This measures the calling process/thread only when running
809 on the specified CPU.
810
811 pid > 0 and cpu == -1
812 This measures the specified process/thread on any CPU.
813
814 pid > 0 and cpu >= 0
815 This measures the specified process/thread only when
816 running on the specified CPU.
817
818 pid == -1 and cpu >= 0
819 This measures all processes/threads on the specified CPU.
820 This requires CAP_PERFMON (since Linux 5.8) or
821 CAP_SYS_ADMIN capability or a
822 /proc/sys/kernel/perf_event_paranoid value of less than 1.
823
824 pid == -1 and cpu == -1
825 This setting is invalid and will return an error.
826 */
827 if (systemTarget_) {
828 pids_.clear();
829 pids_.push_back(-1);
830 } else {
831 if (trackedCommand_) {
832 pids_.push_back(trackedCommand_->GetChildPid());
833 }
834 if (pids_.empty()) {
835 pids_.push_back(0); // no pid means use 0 as self pid
836 }
837 }
838
839 if (cpus_.empty()) {
840 PutAllCpus();
841 }
842
843 // print info tell user which cpu and process we will select.
844 if (pids_.size() == 1 && pids_[0] == -1) {
845 HLOGI("target process: system scope \n");
846 } else {
847 HLOGI("target process: %zu (%s)\n", pids_.size(),
848 (pids_[0] == 0) ? std::to_string(gettid()).c_str() : VectorToString(pids_).c_str());
849 }
850 if (cpus_.size() == 1 && cpus_[0] == -1) {
851 HLOGI("target cpus: %d \n", GetProcessorNum());
852 } else {
853 HLOGI("target cpus: %zu / %d (%s)\n", cpus_.size(), GetProcessorNum(), VectorToString(cpus_).c_str());
854 }
855
856 return true;
857 }
858
CreateFdEvents(void)859 bool PerfEvents::CreateFdEvents(void)
860 {
861 HLOGV("enter");
862
863 // must be some events , or will failed
864 if (eventGroupItem_.empty()) {
865 printf("no event select.\n");
866 return false;
867 }
868
869 // create each fd by cpu and process user select
870 /*
871 https://man7.org/linux/man-pages/man2/perf_event_open.2.html
872
873 (A single event on its own is created with group_fd = -1 and is
874 considered to be a group with only 1 member.)
875 */
876 // Even if there is only one event, it is counted as a group.
877
878 uint fdNumber = 0;
879 uint eventNumber = 0;
880 uint groupNumber = 0;
881 for (auto &eventGroupItem : eventGroupItem_) {
882 /*
883 Explain what is the configuration of the group:
884 Suppose we have 2 Event, 2 PID, and 3 CPU settings
885 According to verification,
886 Group's fd requires the pid to be the same as the cpu, the only difference is event
887 In other words, if you want to bind E1 and E2 to the same group
888 That can only be like this:
889
890 event E1 pid P1 cpu C1 [Group 1]
891 event E1 pid P1 cpu C2 [Group 2]
892 event E1 pid P1 cpu C3 [Group 3]
893
894 event E1 pid P2 cpu C1 [Group 4]
895 event E1 pid P2 cpu C2 [Group 5]
896 event E1 pid P2 cpu C3 [Group 6]
897
898 event E2 pid P1 cpu C1 [Group 1]
899 event E2 pid P1 cpu C2 [Group 2]
900 event E2 pid P1 cpu C3 [Group 3]
901
902 event E2 pid P2 cpu C1 [Group 4]
903 event E2 pid P2 cpu C2 [Group 5]
904 event E2 pid P2 cpu C3 [Group 6]
905 */
906 HLOGV("group %2u. eventGroupItem leader: '%s':", groupNumber++,
907 eventGroupItem.eventItems[0].configName.c_str());
908
909 int groupFdCache[cpus_.size()][pids_.size()];
910 for (size_t i = 0; i < cpus_.size(); i++) { // each cpu
911 for (size_t j = 0; j < pids_.size(); j++) { // each pid
912 // The leader is created first, with group_fd = -1.
913 groupFdCache[i][j] = -1;
914 }
915 }
916
917 uint eventIndex = 0;
918 for (auto &eventItem : eventGroupItem.eventItems) {
919 HLOGV(" - event %2u. eventName: '%s:%s'", eventIndex++, eventItem.typeName.c_str(),
920 eventItem.configName.c_str());
921
922 for (size_t icpu = 0; icpu < cpus_.size(); icpu++) { // each cpu
923 for (size_t ipid = 0; ipid < pids_.size(); ipid++) { // each pid
924 // one fd event group must match same cpu and same pid config (event can be
925 // different)
926 // clang-format off
927 UniqueFd fd = Open(eventItem.attr, pids_[ipid], cpus_[icpu],
928 groupFdCache[icpu][ipid], 0);
929 // clang-format on
930 if (fd < 0) {
931 if (errno == ESRCH) {
932 if (verboseReport_) {
933 printf("pid %d does not exist.\n", pids_[ipid]);
934 }
935 HLOGE("pid %d does not exist.\n", pids_[ipid]);
936 continue;
937 } else {
938 // clang-format off
939 if (verboseReport_) {
940 char errInfo[ERRINFOLEN] = { 0 };
941 strerror_r(errno, errInfo, ERRINFOLEN);
942 printf("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n",
943 eventItem.configName.c_str(), cpus_[icpu], errno, errInfo);
944 }
945 char errInfo[ERRINFOLEN] = { 0 };
946 strerror_r(errno, errInfo, ERRINFOLEN);
947 HLOGE("%s event is not supported by the kernel on cpu %d. reason: %d:%s\n",
948 eventItem.configName.c_str(), cpus_[icpu], errno, errInfo);
949 // clang-format on
950 break; // jump to next cpu
951 }
952 }
953 // after open successed , fill the result
954 // make a new FdItem
955 FdItem &fdItem = eventItem.fdItems.emplace_back();
956 fdItem.fd = move(fd);
957 fdItem.cpu = cpus_[icpu];
958 fdItem.pid = pids_[ipid];
959 fdNumber++;
960
961 // if sampling, mmap ring buffer
962 if (recordCallBack_) {
963 CreateMmap(fdItem, eventItem.attr);
964 }
965 // update group leader
966 if (groupFdCache[icpu][ipid] == -1) {
967 groupFdCache[icpu][ipid] = fdItem.fd.Get();
968 }
969 }
970 }
971 eventNumber++;
972 }
973 }
974
975 if (fdNumber == 0) {
976 HLOGE("open %d fd for %d events", fdNumber, eventNumber);
977 return false;
978 }
979
980 HLOGD("will try read %u events from %u fd (%zu groups):", eventNumber, fdNumber,
981 eventGroupItem_.size());
982
983 return true;
984 }
985
StatReport(const __u64 & durationInSec)986 bool PerfEvents::StatReport(const __u64 &durationInSec)
987 {
988 read_format_no_group readNoGroupValue;
989
990 // only need read when need report
991 HLOGM("eventGroupItem_:%zu", eventGroupItem_.size());
992 __u64 groupId = 0;
993 // clear countEvents data
994 countEvents_.clear();
995 for (const auto &eventGroupItem : eventGroupItem_) {
996 HLOGM("eventItems:%zu", eventGroupItem.eventItems.size());
997 groupId++;
998 for (const auto &eventItem : eventGroupItem.eventItems) {
999 // count event info together (every cpu , every pid)
1000 std::string configName = "";
1001 if (eventItem.attr.exclude_kernel) {
1002 configName = eventItem.configName + ":u";
1003 } else if (eventItem.attr.exclude_user) {
1004 configName = eventItem.configName + ":k";
1005 } else {
1006 configName = eventItem.configName;
1007 }
1008 if (countEvents_.count(configName) == 0) {
1009 auto countEvent = make_unique<CountEvent>(CountEvent {});
1010 countEvents_[configName] = std::move(countEvent);
1011 countEvents_[configName]->userOnly = eventItem.attr.exclude_kernel;
1012 countEvents_[configName]->kernelOnly = eventItem.attr.exclude_user;
1013 }
1014 std::unique_ptr<CountEvent> &countEvent = countEvents_[configName];
1015 HLOGM("eventItem.fdItems:%zu", eventItem.fdItems.size());
1016 for (const auto &fditem : eventItem.fdItems) {
1017 if (read(fditem.fd, &readNoGroupValue, sizeof(readNoGroupValue)) > 0) {
1018 countEvent->eventCount += readNoGroupValue.value;
1019 countEvent->time_enabled += readNoGroupValue.time_enabled;
1020 countEvent->time_running += readNoGroupValue.time_running;
1021 countEvent->id = groupId;
1022 if (durationInSec != 0) {
1023 countEvent->used_cpus =
1024 (countEvent->eventCount / 1e9) / (durationInSec / THOUSANDS);
1025 }
1026 if (verboseReport_) {
1027 printf("%s id:%llu(c%d:p%d) time_enabled:%llu time_running:%llu "
1028 "value:%llu\n",
1029 eventItem.configName.c_str(), readNoGroupValue.id, fditem.cpu,
1030 fditem.pid, readNoGroupValue.time_enabled,
1031 readNoGroupValue.time_running, readNoGroupValue.value);
1032 }
1033 } else {
1034 printf("read failed from event '%s'\n", eventItem.configName.c_str());
1035 }
1036 }
1037 }
1038 }
1039
1040 reportCallBack_(countEvents_);
1041
1042 return true;
1043 }
1044
CreateMmap(const FdItem & item,const perf_event_attr & attr)1045 bool PerfEvents::CreateMmap(const FdItem &item, const perf_event_attr &attr)
1046 {
1047 auto it = cpuMmap_.find(item.cpu);
1048 if (it == cpuMmap_.end()) {
1049 void *rbuf = mmap(nullptr, (1 + mmapPages_) * pageSize_, PROT_READ | PROT_WRITE, MAP_SHARED,
1050 item.fd.Get(), 0);
1051 if (rbuf == MMAP_FAILED) {
1052 char errInfo[ERRINFOLEN] = {0};
1053 strerror_r(errno, errInfo, ERRINFOLEN);
1054 perror("errno:%d, errstr:%s", errno, errInfo);
1055 perror("Fail to call mmap \n");
1056 return false;
1057 }
1058 MmapFd mmapItem;
1059 mmapItem.fd = item.fd.Get();
1060 mmapItem.mmapPage = reinterpret_cast<perf_event_mmap_page *>(rbuf);
1061 mmapItem.buf = reinterpret_cast<uint8_t *>(rbuf) + pageSize_;
1062 mmapItem.bufSize = mmapPages_ * pageSize_;
1063 mmapItem.attr = &attr;
1064 mmapItem.posCallChain = GetCallChainPosInSampleRecord(attr);
1065
1066 cpuMmap_[item.cpu] = mmapItem;
1067 pollFds_.emplace_back(pollfd {mmapItem.fd, POLLIN, 0});
1068 HLOGD("CreateMmap success cpu %d fd %d", item.cpu, mmapItem.fd);
1069 } else {
1070 const MmapFd &mmapItem = it->second;
1071 int rc = ioctl(item.fd.Get(), PERF_EVENT_IOC_SET_OUTPUT, mmapItem.fd);
1072 if (rc != 0) {
1073 HLOGEP("ioctl PERF_EVENT_IOC_SET_OUTPUT (%d -> %d) ", item.fd.Get(), mmapItem.fd);
1074 perror("failed to share mapped buffer\n");
1075 return false;
1076 }
1077 }
1078 return true;
1079 }
1080
GetAttrWithId() const1081 std::vector<AttrWithId> PerfEvents::GetAttrWithId() const
1082 {
1083 std::vector<AttrWithId> result;
1084 HLOGV("eventGroupItem_ %zu :", eventGroupItem_.size());
1085
1086 for (const auto &eventGroupItem : eventGroupItem_) {
1087 HLOGV(" eventItems %zu eventItems:", eventGroupItem.eventItems.size());
1088 for (const auto &eventItem : eventGroupItem.eventItems) {
1089 AttrWithId attrId;
1090 attrId.attr = eventItem.attr;
1091 attrId.name = eventItem.configName;
1092 HLOGV(" fdItems %zu fdItems:", eventItem.fdItems.size());
1093 for (const auto &fdItem : eventItem.fdItems) {
1094 auto &id = attrId.ids.emplace_back(fdItem.GetPrefId());
1095 HLOGV(" eventItem.fdItems GetPrefId %" PRIu64 "", id);
1096 }
1097 result.emplace_back(attrId);
1098 }
1099 }
1100 return result;
1101 }
1102
CalcBufferSize()1103 size_t PerfEvents::CalcBufferSize()
1104 {
1105 size_t bufferSize = MAX_BUFFER_SIZE;
1106 if (!systemTarget_) {
1107 // suppose ring buffer is 4 times as much as mmap
1108 static constexpr int TIMES = 4;
1109 bufferSize = cpuMmap_.size() * mmapPages_ * pageSize_ * TIMES;
1110 if (bufferSize < MIN_BUFFER_SIZE) {
1111 bufferSize = MIN_BUFFER_SIZE;
1112 } else if (bufferSize > MAX_BUFFER_SIZE) {
1113 bufferSize = MAX_BUFFER_SIZE;
1114 }
1115 }
1116 HLOGD("CalcBufferSize return %zu", bufferSize);
1117 return bufferSize;
1118 }
1119
IsRecordInMmap()1120 inline bool PerfEvents::IsRecordInMmap()
1121 {
1122 if (pollFds_.size() > 0) {
1123 if (poll((struct pollfd *)pollFds_.data(), pollFds_.size(), pollTimeOut_) <= 0) {
1124 // time out try again
1125 return false;
1126 }
1127 }
1128 return true;
1129 }
1130
CompareRecordTime(const PerfEvents::MmapFd * left,const PerfEvents::MmapFd * right)1131 static bool CompareRecordTime(const PerfEvents::MmapFd *left, const PerfEvents::MmapFd *right)
1132 {
1133 return left->timestamp > right->timestamp;
1134 }
1135
ReadRecordsFromMmaps()1136 void PerfEvents::ReadRecordsFromMmaps()
1137 {
1138 #ifdef HIPERF_DEBUG_TIME
1139 const auto readKenelStartTime = steady_clock::now();
1140 #endif
1141 // get readable mmap at this time
1142 for (auto &it : cpuMmap_) {
1143 ssize_t dataSize = it.second.mmapPage->data_head - it.second.mmapPage->data_tail;
1144 __sync_synchronize(); // this same as rmb in gcc, after reading mmapPage->data_head
1145 if (dataSize <= 0) {
1146 continue;
1147 }
1148 it.second.dataSize = dataSize;
1149 MmapRecordHeap_.push_back(&(it.second));
1150 }
1151 if (MmapRecordHeap_.empty()) {
1152 return;
1153 }
1154
1155 if (MmapRecordHeap_.size() > 1) {
1156 for (auto &it : MmapRecordHeap_) {
1157 GetRecordFromMmap(*it);
1158 }
1159 std::make_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.end(), CompareRecordTime);
1160
1161 size_t heapSize = MmapRecordHeap_.size();
1162 while (heapSize > 1) {
1163 std::pop_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.begin() + heapSize,
1164 CompareRecordTime);
1165 MoveRecordToBuf(*MmapRecordHeap_[heapSize - 1]);
1166 if (GetRecordFromMmap(*MmapRecordHeap_[heapSize - 1])) {
1167 std::push_heap(MmapRecordHeap_.begin(), MmapRecordHeap_.begin() + heapSize,
1168 CompareRecordTime);
1169 } else {
1170 heapSize--;
1171 }
1172 }
1173 }
1174
1175 while (GetRecordFromMmap(*MmapRecordHeap_.front())) {
1176 MoveRecordToBuf(*MmapRecordHeap_.front());
1177 }
1178 MmapRecordHeap_.clear();
1179 {
1180 std::lock_guard<std::mutex> lk(mtxRrecordBuf_);
1181 recordBufReady_ = true;
1182 }
1183 cvRecordBuf_.notify_one();
1184 #ifdef HIPERF_DEBUG_TIME
1185 recordKernelReadTime_ += duration_cast<milliseconds>(steady_clock::now() - readKenelStartTime);
1186 #endif
1187 }
1188
GetRecordFromMmap(MmapFd & mmap)1189 bool PerfEvents::GetRecordFromMmap(MmapFd &mmap)
1190 {
1191 if (mmap.dataSize <= 0) {
1192 return false;
1193 }
1194
1195 GetRecordFieldFromMmap(mmap, &(mmap.header), mmap.mmapPage->data_tail, sizeof(mmap.header));
1196 if (mmap.header.type != PERF_RECORD_SAMPLE) {
1197 mmap.timestamp = 0;
1198 return true;
1199 }
1200 // in PERF_RECORD_SAMPLE : header + u64 sample_id + u64 ip + u32 pid + u32 tid + u64 time
1201 constexpr size_t timePos = sizeof(perf_event_header) + sizeof(uint64_t) + sizeof(uint64_t) +
1202 sizeof(uint32_t) + sizeof(uint32_t);
1203 GetRecordFieldFromMmap(mmap, &(mmap.timestamp), mmap.mmapPage->data_tail + timePos,
1204 sizeof(mmap.timestamp));
1205 return true;
1206 }
1207
GetRecordFieldFromMmap(MmapFd & mmap,void * dest,size_t pos,size_t size)1208 void PerfEvents::GetRecordFieldFromMmap(MmapFd &mmap, void *dest, size_t pos, size_t size)
1209 {
1210 pos = pos % mmap.bufSize;
1211 size_t tailSize = mmap.bufSize - pos;
1212 size_t copySize = std::min(size, tailSize);
1213 if (memcpy_s(dest, copySize, mmap.buf + pos, copySize) != 0) {
1214 HLOGEP("memcpy_s %p to %p failed. size %zd", mmap.buf + pos, dest, copySize);
1215 }
1216 if (copySize < size) {
1217 size -= copySize;
1218 if (memcpy_s(static_cast<uint8_t *>(dest) + copySize, size, mmap.buf, size) != 0) {
1219 HLOGEP("memcpy_s %p to %p failed. size %zd", mmap.buf,
1220 static_cast<uint8_t *>(dest) + copySize, size);
1221 }
1222 }
1223 }
1224
GetCallChainPosInSampleRecord(const perf_event_attr & attr)1225 size_t PerfEvents::GetCallChainPosInSampleRecord(const perf_event_attr &attr)
1226 {
1227 // reference struct PerfRecordSampleData
1228 int fixedFieldNumber = __builtin_popcountll(
1229 attr.sample_type & (PERF_SAMPLE_IDENTIFIER | PERF_SAMPLE_IP | PERF_SAMPLE_TID |
1230 PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | PERF_SAMPLE_ID |
1231 PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD));
1232 size_t pos = sizeof(perf_event_header) + sizeof(uint64_t) * fixedFieldNumber;
1233 if (attr.sample_type & PERF_SAMPLE_READ) {
1234 pos += sizeof(read_format);
1235 }
1236 return pos;
1237 }
1238
GetStackSizePosInSampleRecord(MmapFd & mmap)1239 size_t PerfEvents::GetStackSizePosInSampleRecord(MmapFd &mmap)
1240 {
1241 size_t pos = mmap.posCallChain;
1242 if (mmap.attr->sample_type & PERF_SAMPLE_CALLCHAIN) {
1243 uint64_t nr = 0;
1244 GetRecordFieldFromMmap(mmap, &nr, mmap.mmapPage->data_tail + pos, sizeof(nr));
1245 pos += (sizeof(nr) + nr * sizeof(uint64_t));
1246 }
1247 if (mmap.attr->sample_type & PERF_SAMPLE_RAW) {
1248 uint32_t raw_size = 0;
1249 GetRecordFieldFromMmap(mmap, &raw_size, mmap.mmapPage->data_tail + pos, sizeof(raw_size));
1250 pos += (sizeof(raw_size) + raw_size);
1251 }
1252 if (mmap.attr->sample_type & PERF_SAMPLE_BRANCH_STACK) {
1253 uint64_t bnr = 0;
1254 GetRecordFieldFromMmap(mmap, &bnr, mmap.mmapPage->data_tail + pos, sizeof(bnr));
1255 pos += (sizeof(bnr) + bnr * sizeof(perf_branch_entry));
1256 }
1257 if (mmap.attr->sample_type & PERF_SAMPLE_REGS_USER) {
1258 uint64_t user_abi = 0;
1259 GetRecordFieldFromMmap(mmap, &user_abi, mmap.mmapPage->data_tail + pos, sizeof(user_abi));
1260 pos += sizeof(user_abi);
1261 if (user_abi > 0) {
1262 uint64_t reg_nr = __builtin_popcountll(mmap.attr->sample_regs_user);
1263 pos += reg_nr * sizeof(uint64_t);
1264 }
1265 }
1266 return pos;
1267 }
1268
CutStackAndMove(MmapFd & mmap)1269 bool PerfEvents::CutStackAndMove(MmapFd &mmap)
1270 {
1271 constexpr uint32_t alignSize = 64;
1272 if (!(mmap.attr->sample_type & PERF_SAMPLE_STACK_USER)) {
1273 return false;
1274 }
1275 size_t stackSizePos = GetStackSizePosInSampleRecord(mmap);
1276 uint64_t stackSize = 0;
1277 GetRecordFieldFromMmap(mmap, &stackSize, mmap.mmapPage->data_tail + stackSizePos,
1278 sizeof(stackSize));
1279 if (stackSize == 0) {
1280 return false;
1281 }
1282 size_t dynSizePos = stackSizePos + sizeof(uint64_t) + stackSize;
1283 uint64_t dynSize = 0;
1284 GetRecordFieldFromMmap(mmap, &dynSize, mmap.mmapPage->data_tail + dynSizePos, sizeof(dynSize));
1285 uint64_t newStackSize = std::min(ALIGN(dynSize, alignSize), stackSize);
1286 if (newStackSize >= stackSize) {
1287 return false;
1288 }
1289 HLOGM("stackSize %" PRIx64 " dynSize %" PRIx64 " newStackSize %" PRIx64 "\n", stackSize, dynSize, newStackSize);
1290 // move and cut stack_data
1291 // mmap: |<+++copy1+++>|<++++++copy2++++++>|<---------------cut--------------->|<+++copy3+++>|
1292 // ^ ^ ^ ^
1293 // new_header stackSizePos <stackSize-dynSize> dynSizePos
1294 uint16_t recordSize = mmap.header.size;
1295 mmap.header.size -= stackSize - newStackSize; // reduce the stack size
1296 uint8_t *buf = recordBuf_->AllocForWrite(mmap.header.size);
1297 // copy1: new_header
1298 if (memcpy_s(buf, sizeof(perf_event_header), &(mmap.header), sizeof(perf_event_header)) != 0) {
1299 HLOGEP("memcpy_s %p to %p failed. size %zd", &(mmap.header), buf,
1300 sizeof(perf_event_header));
1301 }
1302 size_t copyPos = sizeof(perf_event_header);
1303 size_t copySize = stackSizePos - sizeof(perf_event_header) + sizeof(stackSize) + newStackSize;
1304 // copy2: copy stack_size, data[stack_size],
1305 GetRecordFieldFromMmap(mmap, buf + copyPos, mmap.mmapPage->data_tail + copyPos, copySize);
1306 copyPos += copySize;
1307 // copy3: copy dyn_size
1308 GetRecordFieldFromMmap(mmap, buf + copyPos, mmap.mmapPage->data_tail + dynSizePos,
1309 recordSize - dynSizePos);
1310 // update stack_size
1311 if (memcpy_s(buf + stackSizePos, sizeof(stackSize), &(newStackSize), sizeof(newStackSize)) != 0) {
1312 HLOGEP("memcpy_s %p to %p failed. size %zd", &(newStackSize), buf + stackSizePos, sizeof(newStackSize));
1313 }
1314 recordBuf_->EndWrite();
1315 __sync_synchronize();
1316 mmap.mmapPage->data_tail += recordSize;
1317 mmap.dataSize -= recordSize;
1318 return true;
1319 }
1320
MoveRecordToBuf(MmapFd & mmap)1321 void PerfEvents::MoveRecordToBuf(MmapFd &mmap)
1322 {
1323 uint8_t *buf = nullptr;
1324 if (mmap.header.type == PERF_RECORD_SAMPLE) {
1325 if (recordBuf_->GetFreeSize() <= BUFFER_CRITICAL_LEVEL) {
1326 lostSamples_++;
1327 HLOGD("BUFFER_CRITICAL_LEVEL: lost sample record");
1328 goto RETURN;
1329 }
1330 if (CutStackAndMove(mmap)) {
1331 return;
1332 }
1333 } else if (mmap.header.type == PERF_RECORD_LOST) {
1334 // in PERF_RECORD_LOST : header + u64 id + u64 lost
1335 constexpr size_t lostPos = sizeof(perf_event_header) + sizeof(uint64_t);
1336 uint64_t lost = 0;
1337 GetRecordFieldFromMmap(mmap, &lost, mmap.mmapPage->data_tail + lostPos, sizeof(lost));
1338 lostSamples_ += lost;
1339 HLOGD("PERF_RECORD_LOST: lost sample record");
1340 goto RETURN;
1341 }
1342
1343 if ((buf = recordBuf_->AllocForWrite(mmap.header.size)) == nullptr) {
1344 // this record type must be Non-Sample
1345 lostNonSamples_++;
1346 HLOGD("alloc buffer failed: lost non-sample record");
1347 goto RETURN;
1348 }
1349
1350 GetRecordFieldFromMmap(mmap, buf, mmap.mmapPage->data_tail, mmap.header.size);
1351 recordBuf_->EndWrite();
1352 RETURN:
1353 __sync_synchronize();
1354 mmap.mmapPage->data_tail += mmap.header.size;
1355 mmap.dataSize -= mmap.header.size;
1356 }
1357
ReadRecordFromBuf()1358 void PerfEvents::ReadRecordFromBuf()
1359 {
1360 HLOGV("enter");
1361
1362 const perf_event_attr *attr = GetDefaultAttr();
1363 uint8_t *p = nullptr;
1364
1365 while (readRecordThreadRunning_) {
1366 {
1367 std::unique_lock<std::mutex> lk(mtxRrecordBuf_);
1368 cvRecordBuf_.wait(lk, [this] {
1369 if (recordBufReady_) {
1370 recordBufReady_ = false;
1371 return true;
1372 }
1373 return !readRecordThreadRunning_;
1374 });
1375 }
1376 while ((p = recordBuf_->GetReadData()) != nullptr) {
1377 uint32_t *type = reinterpret_cast<uint32_t *>(p);
1378 #ifdef HIPERF_DEBUG_TIME
1379 const auto readingStartTime_ = steady_clock::now();
1380 #endif
1381 #if !HIDEBUG_SKIP_CALLBACK
1382 recordCallBack_(GetPerfEventRecord(*type, p, *attr));
1383 #endif
1384 recordEventCount_++;
1385 #ifdef HIPERF_DEBUG_TIME
1386 recordCallBackTime_ +=
1387 duration_cast<milliseconds>(steady_clock::now() - readingStartTime_);
1388 #endif
1389 recordBuf_->EndRead();
1390 }
1391 }
1392 HLOGD("exit because trackStoped");
1393
1394 // read the data left over in buffer
1395 while ((p = recordBuf_->GetReadData()) != nullptr) {
1396 uint32_t *type = reinterpret_cast<uint32_t *>(p);
1397 #ifdef HIPERF_DEBUG_TIME
1398 const auto readingStartTime_ = steady_clock::now();
1399 #endif
1400 #if !HIDEBUG_SKIP_CALLBACK
1401 recordCallBack_(GetPerfEventRecord(*type, p, *attr));
1402 #endif
1403 recordEventCount_++;
1404 #ifdef HIPERF_DEBUG_TIME
1405 recordCallBackTime_ += duration_cast<milliseconds>(steady_clock::now() - readingStartTime_);
1406 #endif
1407 recordBuf_->EndRead();
1408 }
1409 HLOGD("read all records from buffer");
1410 }
1411
HaveTargetsExit(const std::chrono::steady_clock::time_point & startTime)1412 bool PerfEvents::HaveTargetsExit(const std::chrono::steady_clock::time_point &startTime)
1413 {
1414 if (systemTarget_) {
1415 return false;
1416 }
1417 if (trackedCommand_) {
1418 if (trackedCommand_->GetState() < TrackedCommand::State::COMMAND_STARTED) {
1419 return false; // not start yet
1420 }
1421 int wstatus;
1422 if (trackedCommand_->WaitCommand(wstatus)) {
1423 milliseconds usedMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime);
1424 printf("tracked command(%s) has exited (total %" PRId64 " ms)\n",
1425 trackedCommand_->GetCommandName().c_str(), (uint64_t)usedMsTick.count());
1426 return true;
1427 }
1428 return false;
1429 }
1430
1431 for (auto it = pids_.begin(); it != pids_.end();) {
1432 if (IsDir("/proc/" + std::to_string(*it))) {
1433 it++;
1434 } else {
1435 it = pids_.erase(it);
1436 }
1437 }
1438 if (pids_.empty()) {
1439 milliseconds usedMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime);
1440 printf("tracked processes have exited (total %" PRId64 " ms)\n", (uint64_t)usedMsTick.count());
1441 return true;
1442 }
1443 return false;
1444 }
1445
RecordLoop()1446 void PerfEvents::RecordLoop()
1447 {
1448 HLOGV("enter");
1449
1450 // calc the time
1451 const auto startTime = steady_clock::now();
1452 const auto endTime = startTime + timeOut_;
1453 milliseconds usedTimeMsTick {};
1454
1455 while (g_trackRunning) {
1456 // time check point
1457 const auto thisTime = steady_clock::now();
1458
1459 if (IsRecordInMmap()) {
1460 ReadRecordsFromMmaps();
1461 }
1462
1463 if (HaveTargetsExit(startTime)) {
1464 break;
1465 }
1466
1467 if (thisTime >= endTime) {
1468 usedTimeMsTick = duration_cast<milliseconds>(thisTime - startTime);
1469 printf("Timeout exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count());
1470 if (trackedCommand_) {
1471 trackedCommand_->Stop();
1472 }
1473 break;
1474 }
1475 }
1476 ReadRecordsFromMmaps();
1477
1478 if (!g_trackRunning) {
1479 // for user interrupt situation, print time statistic
1480 usedTimeMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime);
1481 printf("User interrupt exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count());
1482 }
1483 }
1484
StatLoop()1485 void PerfEvents::StatLoop()
1486 {
1487 HLOGV("enter");
1488
1489 // calc the time
1490 const auto startTime = steady_clock::now();
1491 const auto endTime = startTime + timeOut_;
1492 auto nextReportTime = startTime + timeReport_;
1493 milliseconds usedTimeMsTick {};
1494 __u64 durationInSec = 0;
1495 int64_t thesholdTimeInMs = 2 * HUNDREDS;
1496
1497 while (g_trackRunning) {
1498 // time check point
1499 const auto thisTime = steady_clock::now();
1500 if (timeReport_ != milliseconds::zero()) {
1501 // stat cmd
1502 if (thisTime >= nextReportTime) {
1503 // only for log or debug?
1504 usedTimeMsTick = duration_cast<milliseconds>(thisTime - startTime);
1505 durationInSec = usedTimeMsTick.count();
1506 auto lefTimeMsTick = duration_cast<milliseconds>(endTime - thisTime);
1507 printf("\nReport at %" PRId64 " ms (%" PRId64 " ms left):\n",
1508 (uint64_t)usedTimeMsTick.count(), (uint64_t)lefTimeMsTick.count());
1509 // end of comments
1510 nextReportTime += timeReport_;
1511 StatReport(durationInSec);
1512 }
1513 }
1514
1515 if (HaveTargetsExit(startTime)) {
1516 break;
1517 }
1518
1519 if (thisTime >= endTime) {
1520 usedTimeMsTick = duration_cast<milliseconds>(thisTime - startTime);
1521 durationInSec = usedTimeMsTick.count();
1522 printf("Timeout exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count());
1523 if (trackedCommand_) {
1524 trackedCommand_->Stop();
1525 }
1526 break;
1527 }
1528
1529 // lefttime > 200ms sleep 100ms, else sleep 200us
1530 uint64_t defaultSleepUs = 2 * HUNDREDS; // 200us
1531 if (timeReport_ == milliseconds::zero()
1532 && (timeOut_.count() * THOUSANDS) > thesholdTimeInMs) {
1533 milliseconds leftTimeMsTmp = duration_cast<milliseconds>(endTime - thisTime);
1534 if (leftTimeMsTmp.count() > thesholdTimeInMs) {
1535 defaultSleepUs = HUNDREDS * THOUSANDS; // 100ms
1536 }
1537 }
1538 std::this_thread::sleep_for(microseconds(defaultSleepUs));
1539 }
1540
1541 if (!g_trackRunning) {
1542 // for user interrupt situation, print time statistic
1543 usedTimeMsTick = duration_cast<milliseconds>(steady_clock::now() - startTime);
1544 printf("User interrupt exit (total %" PRId64 " ms)\n", (uint64_t)usedTimeMsTick.count());
1545 }
1546
1547 if (timeReport_ == milliseconds::zero()) {
1548 StatReport(durationInSec);
1549 }
1550 }
1551
GetTypeName(perf_type_id type_id)1552 const std::string PerfEvents::GetTypeName(perf_type_id type_id)
1553 {
1554 auto it = PERF_TYPES.find(type_id);
1555 if (it != PERF_TYPES.end()) {
1556 return it->second;
1557 } else {
1558 return "<not found>";
1559 }
1560 }
1561 } // namespace HiPerf
1562 } // namespace Developtools
1563 } // namespace OHOS
1564