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