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