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