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