• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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