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