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