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