diff --git a/include/pcerrc.h b/include/pcerrc.h index b1c2736f2dba2716a9d203521c0443f249451bd1..4151cfc5273b92977ddeb0f6f37346ba05fcb753 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -118,6 +118,10 @@ extern "C" { #define LIBPERF_ERR_INVALID_PMU_FILE 1071 #define LIBPERF_ERR_NOT_SUPPORT_PCIE_PORT 1072 #define LIBPERF_ERR_INVALID_EVTATTR 1073 +#define LIBPERF_ERR_COUNT_MMAP_IS_NULL 1074 +#define LIBPERF_ERR_ENABLE_USER_ACCESS_FAILED 1075 +#define LIBPERF_ERR_ALLOCATE_REGISTER_FAILED 1076 +#define LIBPERF_ERR_CHECK_USER_ACCESS 1077 #define UNKNOWN_ERROR 9999 diff --git a/include/pmu.h b/include/pmu.h index 4a3ba2fb752a9a0273311e0dc745584f022bb2d4..89aed63dbb71b10b6e9f64b225bb8759399c4b30 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -173,6 +173,9 @@ struct PmuAttr { // if use cgroup function. use the cgroup name in the cgroupNamelist to apply all events in the Event list char** cgroupNameList; unsigned numCgroup; + + // enable user access counting for current process + unsigned enableUserAccess : 1; }; enum PmuTraceType { diff --git a/pmu/evt.cpp b/pmu/evt.cpp index 0afa37cafddfe304ab41506b4764deda9305d47c..e401c7ccdcb9b419e98ef27310a63cdaefdb0817 100644 --- a/pmu/evt.cpp +++ b/pmu/evt.cpp @@ -93,47 +93,95 @@ __u64 KUNPENG_PMU::ReadOnce(__u64 *head) char charHead[1]; } pointerUnion = {.charHead = {0}}; - switch (static_cast(sizeof(*head))) { - case HEAD_SIZE::HEAD_SIZE_ONE: - asm volatile("ldarb %w0, %1" - : "=r"(*(__u8 __attribute__((__may_alias__)) *)pointerUnion.charHead) - : "Q"(*head) - : "memory"); - break; - case HEAD_SIZE::HEAD_SIZE_TWO: - asm volatile("ldarh %w0, %1" - : "=r"(*(__u16 __attribute__((__may_alias__)) *)pointerUnion.charHead) - : "Q"(*head) - : "memory"); - break; - case HEAD_SIZE::HEAD_SIZE_FOUR: - asm volatile("ldar %w0, %1" - : "=r"(*(__u32 __attribute__((__may_alias__)) *)pointerUnion.charHead) - : "Q"(*head) - : "memory"); - break; - case HEAD_SIZE::HEAD_SIZE_EIGHT: #ifdef IS_X86 - asm volatile("mov %0, %1" - : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) - : "Q"(*head) - : "memory"); + asm volatile("mov %0, %1" + : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); #elif defined(IS_ARM) - asm volatile("ldar %0, %1" - : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) - : "Q"(*head) - : "memory"); + asm volatile("ldar %0, %1" + : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); #elif defined(IS_RISCV64) - asm volatile("ld %0, %1\nfence" - : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) - : "m"(*head) - : "memory"); + asm volatile("ld %0, %1\nfence" + : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "m"(*head) + : "memory"); #else - #error "Unsupported architecture" +#error "Unsupported architecture" #endif - break; - default: - break; - } + + return pointerUnion.val; +} + +__s64 KUNPENG_PMU::ReadOnce(__s64 *head) +{ + union { + typeof(*head) val; + char charHead[1]; + } pointerUnion = {.charHead = {0}}; + +#ifdef IS_X86 + asm volatile("mov %0, %1" + : "=r"(*(__s64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); +#elif defined(IS_ARM) + asm volatile("ldar %0, %1" + : "=r"(*(__s64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); +#elif defined(IS_RISCV64) + asm volatile("ld %0, %1\nfence" + : "=r"(*(__s64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "m"(*head) + : "memory"); +#else +#error "Unsupported architecture" +#endif + + return pointerUnion.val; +} + +__u32 KUNPENG_PMU::ReadOnce(__u32 *head) +{ + union { + typeof(*head) val; + char charHead[1]; + } pointerUnion = {.charHead = {0}}; + + asm volatile("ldar %w0, %1" + : "=r"(*(__u32 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); + return pointerUnion.val; +} + +__u16 KUNPENG_PMU::ReadOnce(__u16 *head) +{ + union { + typeof(*head) val; + char charHead[1]; + } pointerUnion = {.charHead = {0}}; + + asm volatile("ldarh %w0, %1" + : "=r"(*(__u16 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); + return pointerUnion.val; +} + +__u8 KUNPENG_PMU::ReadOnce(__u8 *head) +{ + union { + typeof(*head) val; + char charHead[1]; + } pointerUnion = {.charHead = {0}}; + + asm volatile("ldarb %w0, %1" + : "=r"(*(__u8 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); return pointerUnion.val; } diff --git a/pmu/evt.h b/pmu/evt.h index 383576ac10d9be06b25d53a9f39016560bec11fe..a268d00e102744e5db34485a775e01205bb29bcc 100644 --- a/pmu/evt.h +++ b/pmu/evt.h @@ -100,6 +100,10 @@ protected: bool needTryExcludeKernel; }; int PerfEventOpen(struct perf_event_attr* attr, pid_t pid, int cpu, int groupFd, unsigned long flags); +__s64 ReadOnce(__s64 *head); __u64 ReadOnce(__u64 *head); +__u32 ReadOnce(__u32 *head); +__u16 ReadOnce(__u16 *head); +__u8 ReadOnce(__u8 *head); } // namespace KUNPENG_PMU #endif diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp index b96a4ad2571d445829a4e06d17f39fb438531335..c9a5dac44bc46e1e7d053f7ca778677be8d9c0d6 100644 --- a/pmu/evt_list.cpp +++ b/pmu/evt_list.cpp @@ -105,7 +105,7 @@ int KUNPENG_PMU::EvtList::Init(const bool groupEnable, const std::shared_ptrtid > 0) { + if (proc->tid >= 0) { procMap[proc->tid] = proc; } } diff --git a/pmu/perf_counter.cpp b/pmu/perf_counter.cpp index 598a9fd6e31724c88afae63b1c4c087c3985d6f1..3ad6339caa1932d5b7c7c48a594b236859b4268e 100644 --- a/pmu/perf_counter.cpp +++ b/pmu/perf_counter.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include "pmu.h" @@ -28,6 +29,8 @@ #include "pcerr.h" #include "log.h" #include "perf_counter.h" +#include "read_reg.h" +#include "common.h" using namespace std; using namespace pcerr; @@ -67,20 +70,96 @@ int KUNPENG_PMU::PerfCounter::Read(EventData &eventData) return SUCCESS; } +namespace KUNPENG_PMU { +static int PerfMmapReadSelf(const std::shared_ptr &countMmap, struct ReadFormat &perfCountValue) +{ + uint32_t seq; + uint32_t idx; + uint32_t timeMult = 0; + uint32_t timeShift = 0; + uint64_t cnt = 0; + uint64_t cyc = 0; + uint64_t timeOffset = 0; + uint64_t timeCycles = 0; + uint64_t timeMask = ~0ULL; + auto pc = countMmap->base; + if (!pc) { + return LIBPERF_ERR_COUNT_MMAP_IS_NULL; + } + if (!pc->cap_user_rdpmc) { + return LIBPERF_ERR_ENABLE_USER_ACCESS_FAILED; + } + + do { + seq = ReadOnce(&pc->lock); + Barrier(); + + perfCountValue.timeEnabled = ReadOnce(&pc->time_enabled); + perfCountValue.timeRunning = ReadOnce(&pc->time_running); + + if (pc->cap_user_rdpmc && perfCountValue.timeEnabled != perfCountValue.timeRunning) { + cyc = ReadTimestamp(); + timeMult = ReadOnce(&pc->time_mult); + timeShift = ReadOnce(&pc->time_shift); + timeOffset = ReadOnce(&pc->time_offset); + + if (pc->cap_user_time_short) { + timeCycles = ReadOnce(&pc->time_cycles); + timeMask = ReadOnce(&pc->time_mask); + } + } + + idx = ReadOnce(&pc->index); + cnt = ReadOnce(&pc->offset); + if (pc->cap_user_rdpmc && idx) { + // read the reg mapped by the countMmap->base->idx + uint64_t eventCount = ReadPerfCounter(idx - 1); + uint16_t width = ReadOnce(&pc->pmc_width); + eventCount <<= 64 - width; + eventCount >>= 64 - width; + cnt += eventCount; + } else { + return LIBPERF_ERR_ALLOCATE_REGISTER_FAILED; + } + Barrier(); + } while (ReadOnce(&pc->lock) != seq); + + if (perfCountValue.timeEnabled != perfCountValue.timeRunning) { + uint64_t delta; + cyc = timeCycles + ((cyc - timeCycles) & timeMask); + delta = timeOffset + MulU64U32Shr(cyc, timeMult, timeShift); + perfCountValue.timeEnabled += delta; + if (idx) { + perfCountValue.timeRunning += delta; + } + } + perfCountValue.value = cnt; + return SUCCESS; +} +} // namespace KUNPENG_PMU + int KUNPENG_PMU::PerfCounter::ReadSingleEvent(std::vector &data) { ReadFormat perfCountValue; - int len = read(this->fd, &perfCountValue, sizeof(perfCountValue)); - if (len < 0) { - New(UNKNOWN_ERROR, strerror(errno)); - return UNKNOWN_ERROR; + if (this->evt->config1 & REQUEST_USER_ACCESS) { + int err = PerfMmapReadSelf(this->countMmap, perfCountValue); + if (err != SUCCESS) { + return err; + } + } else { + int len = read(this->fd, &perfCountValue, sizeof(perfCountValue)); + if (len < 0) { + New(UNKNOWN_ERROR, strerror(errno)); + return UNKNOWN_ERROR; + } } + if (accumCount.empty()) { accumCount.assign(1, 0); } - - int err = CountValueToData(perfCountValue.value, perfCountValue.timeEnabled, - perfCountValue.timeRunning, accumCount[0], data); + + int err = CountValueToData( + perfCountValue.value, perfCountValue.timeEnabled, perfCountValue.timeRunning, accumCount[0], data); if (err != SUCCESS) { return err; } @@ -143,9 +222,12 @@ int KUNPENG_PMU::PerfCounter::CountValueToData(const __u64 value, const __u64 ti // counting value (https://perf.wiki.kernel.org/index.php/Tutorial) double percent = 0.0; uint64_t increCount; - if ((value == accumCount) || (timeRunning == running)) { + if (this->evt->config1 & REQUEST_USER_ACCESS) { + percent = 1; + increCount = static_cast(value - accumCount); + } else if ((value == accumCount) || (timeRunning == running)) { percent = -1; - increCount = 0; + increCount = 0; } else { percent = static_cast(timeEnabled - enabled) / static_cast(timeRunning - running); increCount = static_cast((value - accumCount)* percent); @@ -173,7 +255,17 @@ int KUNPENG_PMU::PerfCounter::CountValueToData(const __u64 value, const __u64 ti */ int KUNPENG_PMU::PerfCounter::Init(const bool groupEnable, const int groupFd, const int resetOutputFd) { - return this->MapPerfAttr(groupEnable, groupFd); + int err = SUCCESS; + if (this->evt->config1 & REQUEST_USER_ACCESS) { // user access + err = this->MapPerfAttrUserAccess(); + if (err != SUCCESS) { + return err; + } + err = this->Mmap(); + return err; + } + err = this->MapPerfAttr(groupEnable, groupFd); + return err; } int KUNPENG_PMU::PerfCounter::MapPerfAttr(const bool groupEnable, const int groupFd) @@ -251,6 +343,44 @@ int KUNPENG_PMU::PerfCounter::MapPerfAttr(const bool groupEnable, const int grou return SUCCESS; } +int KUNPENG_PMU::PerfCounter::MapPerfAttrUserAccess() +{ + struct perf_event_attr attr; + memset(&attr, 0, sizeof(attr)); + attr.size = sizeof(struct perf_event_attr); + attr.type = this->evt->type; + attr.config = this->evt->config; + attr.config1 = this->evt->config1; + this->fd = PerfEventOpen(&attr, this->pid, this->cpu, -1, 0); + DBG_PRINT("type: %d cpu: %d config: %llx config1: %llx myfd: %d \n", + attr.type, + this->cpu, + attr.config, + attr.config1, + this->fd); + if (__glibc_unlikely(this->fd < 0)) { + return MapErrno(errno); + } + return SUCCESS; +} + +int KUNPENG_PMU::PerfCounter::Mmap() +{ + this->countMmap = std::make_shared(); + this->countMmap->prev = 0; + this->countMmap->mask = -1; + void *currentMap = + mmap(NULL, COUNT_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, this->fd, 0); + if (__glibc_unlikely(currentMap == MAP_FAILED)) { + this->countMmap->base = nullptr; + close(this->fd); + return LIBPERF_ERR_FAIL_MMAP; + } + this->countMmap->base = static_cast(currentMap); + this->countMmap->fd = this->fd; + return SUCCESS; +} + /** * Enable */ @@ -285,4 +415,15 @@ int KUNPENG_PMU::PerfCounter::Reset() return SUCCESS; } return PerfEvt::Reset(); +} + +int KUNPENG_PMU::PerfCounter::Close() +{ + if (this->countMmap && this->countMmap->base && this->countMmap->base != MAP_FAILED) { + munmap(this->countMmap->base, COUNT_PAGE_SIZE); + } + if (this->fd > 0) { + close(this->fd); + } + return SUCCESS; } \ No newline at end of file diff --git a/pmu/perf_counter.h b/pmu/perf_counter.h index 31e97aeac1f05ce053c67ad125b62509266d9659..0fe1e3d3f1e578b700c01b53bb77d2fdf86d3aeb 100644 --- a/pmu/perf_counter.h +++ b/pmu/perf_counter.h @@ -19,9 +19,12 @@ #include #include #include +#include #include "evt.h" #include "pmu_event.h" +#define REQUEST_USER_ACCESS 0x2 + struct ReadFormat { __u64 value; __u64 timeEnabled; @@ -30,6 +33,7 @@ struct ReadFormat { }; namespace KUNPENG_PMU { + static constexpr int COUNT_PAGE_SIZE = 4096; class PerfCounter : public PerfEvt { public: using PerfEvt::PerfEvt; @@ -41,6 +45,7 @@ namespace KUNPENG_PMU { int Enable() override; int Disable() override; int Reset() override; + int Close() override; private: enum class GroupStatus @@ -49,7 +54,8 @@ namespace KUNPENG_PMU { GROUP_LEADER, GROUP_MEMBER }; - + int Mmap(); + int MapPerfAttrUserAccess(); int CountValueToData(const __u64 value, const __u64 timeEnabled, const __u64 timeRunning, __u64 &accumCount, std::vector &data); int ReadSingleEvent(std::vector &data); @@ -63,6 +69,8 @@ namespace KUNPENG_PMU { std::vector<__u64> accumCount; int groupFd = 0; GroupStatus groupStatus = GroupStatus::NO_GROUP; + // reg index is stored in countMmap->base + std::shared_ptr countMmap = nullptr; }; } // namespace KUNPENG_PMU #endif diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index 2340dd25c86cbcb04e578f691fbf098a023f978b..715db5227c365ac128d61a2d92f64b351829fdc1 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -40,6 +40,8 @@ static pair uncoreEventPair; static std::set onLineCpuIds; static string cgroupDir = "/sys/fs/cgroup/"; +#define REQUEST_USER_ACCESS 0x2 + struct PmuTaskAttr* AssignPmuTaskParam(PmuTaskType collectType, struct PmuAttr *attr); static int PmuCollectStart(const int pd) @@ -75,12 +77,12 @@ static int CheckCpuList(unsigned numCpu, int* cpuList) return LIBPERF_ERR_INVALID_CPULIST; } for (int i = 0; i < numCpu; i++) { - if (cpuList[i] < 0 || cpuList[i] >= MAX_CPU_NUM) { + if (cpuList[i] < -1 || cpuList[i] >= MAX_CPU_NUM) { string errMsg = "Invalid cpu id: " + to_string(cpuList[i]) + ", Please check cpu config parameter."; New(LIBPERF_ERR_INVALID_CPULIST, errMsg); return LIBPERF_ERR_INVALID_CPULIST; } - if (!onLineCpus.count(cpuList[i])) { + if (cpuList[i] != -1 && !onLineCpus.count(cpuList[i])) { string errMsg = "OffLine cpu id: " + to_string(cpuList[i]); New(LIBPERF_ERR_INVALID_CPULIST, errMsg); return LIBPERF_ERR_INVALID_CPULIST; @@ -262,9 +264,37 @@ static int CheckCollectTypeConfig(enum PmuTaskType collectType, struct PmuAttr * return SUCCESS; } +static int CheckUserAccess(enum PmuTaskType collectType, struct PmuAttr *attr) +{ + if (!attr->enableUserAccess) { + return SUCCESS; + } + if (attr->numPid != 1 || attr->pidList[0] != 0) { + New(LIBPERF_ERR_CHECK_USER_ACCESS, "The pidList is incorrectly set!"); + return LIBPERF_ERR_CHECK_USER_ACCESS; + } + if (attr->numCpu != 1 || attr->cpuList[0] != -1) { + New(LIBPERF_ERR_CHECK_USER_ACCESS, "The cpuList is incorrectly set!"); + return LIBPERF_ERR_CHECK_USER_ACCESS; + } + if (collectType != COUNTING) { + New(LIBPERF_ERR_CHECK_USER_ACCESS, "User Access only supports count!"); + return LIBPERF_ERR_CHECK_USER_ACCESS; + } + if (attr->evtAttr != nullptr) { + New(LIBPERF_ERR_CHECK_USER_ACCESS, "User Access doesn't support event group!"); + return LIBPERF_ERR_CHECK_USER_ACCESS; + } + return SUCCESS; +} + static int CheckAttr(enum PmuTaskType collectType, struct PmuAttr *attr) { - auto err = CheckCpuList(attr->numCpu, attr->cpuList); + auto err = CheckUserAccess(collectType, attr); + if (err != SUCCESS) { + return err; + } + err = CheckCpuList(attr->numCpu, attr->cpuList); if (err != SUCCESS) { return err; } @@ -952,7 +982,9 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att if (cgroupName != nullptr) { taskParam->pmuEvt->cgroupName = cgroupName; } - + if (attr->enableUserAccess) { + taskParam->pmuEvt->config1 = REQUEST_USER_ACCESS; + } return taskParam.release(); } diff --git a/pmu/pmu_event.h b/pmu/pmu_event.h index 557b7a8749070c44fbff3d59dec677f0907da4b0..981d13626feca95b6389539a34bf0a2761c255ed 100644 --- a/pmu/pmu_event.h +++ b/pmu/pmu_event.h @@ -166,7 +166,7 @@ struct PerfRecordExit { struct PerfMmap { struct perf_event_mmap_page* base; - __u64 mask; + int mask; int fd; __u64 prev; __u64 start; diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index 7a9672d30f562088d0eb7f2939df32ba6d91b84e..b956e7e1b3e78e9f2ec3665899791eb5edfe09ed 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -944,7 +944,7 @@ namespace KUNPENG_PMU { SetWarn(LIBPERF_WARN_FAIL_GET_PROC, "process not found: " + std::to_string(childTidList[j])); continue; } - procTopo->isMain = masterPid == procTopo->tid; + procTopo->isMain = (masterPid == procTopo->tid || masterPid == 0); foundProc = true; DBG_PRINT("Add to proc map: %d\n", childTidList[j]); procTopoList.emplace_back(shared_ptr(procTopo, FreeProcTopo)); diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index 28931478500a13b2a0e88e989cb3aaf8f2fa1b22..0518975c50116af8bf226786073cca71c5a9124a 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -110,7 +110,7 @@ int KUNPENG_PMU::PerfSampler::Mmap() int mmapLen = (SAMPLE_PAGES + 1) * SAMPLE_PAGE_SIZE; auto mask = mmapLen - SAMPLE_PAGE_SIZE - 1; this->sampleMmap->prev = 0; - this->sampleMmap->mask = static_cast<__u64>(mask); + this->sampleMmap->mask = mask; void *currentMap = mmap(NULL, this->sampleMmap->mask + 1 + SAMPLE_PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (__glibc_unlikely(currentMap == MAP_FAILED)) { diff --git a/util/pcerr.cpp b/util/pcerr.cpp index 2252995da002331e2d87eb1b77359a3ea827046a..eba1369f344b90f43dc678a480d4777383209f7e 100644 --- a/util/pcerr.cpp +++ b/util/pcerr.cpp @@ -61,6 +61,10 @@ namespace pcerr { {LIBPERF_ERR_INVALID_PMU_FILE, "invalid pmu file handler"}, {LIBPERF_ERR_OPEN_INVALID_FILE, "failed to open file"}, {LIBPERF_ERR_INVALID_EVTATTR, "invalid evtAttr list"}, + {LIBPERF_ERR_COUNT_MMAP_IS_NULL, "Count mmap page is null!"}, + {LIBPERF_ERR_ENABLE_USER_ACCESS_FAILED, "Enable user access failed!"}, + {LIBPERF_ERR_ALLOCATE_REGISTER_FAILED, "Allocate register failed!"}, + {LIBPERF_ERR_CHECK_USER_ACCESS, "Check user access failed!"} }; static std::unordered_map warnMsgs = { {LIBPERF_WARN_CTXID_LOST, "Some SPE context packets are not found in the traces."}, diff --git a/util/process_map.cpp b/util/process_map.cpp index d419884db416d676e206115f0e5b48474e21d4fa..9875e016f143d9f2aae1a7f28379d62880277203 100644 --- a/util/process_map.cpp +++ b/util/process_map.cpp @@ -119,6 +119,9 @@ struct ProcTopology *GetProcTopology(pid_t pid) { unique_ptr procTopo(new ProcTopology{0}, FreeProcTopo); procTopo->tid = pid; + if (pid == 0) { + return procTopo.release(); + } try { // Get tgid, i.e., process id. procTopo->pid = GetTgid(pid); @@ -201,6 +204,12 @@ bool GetChildTidRecursive(const char *dirPath, int **childTidList, int *count) int *GetChildTid(int pid, int *numChild) { int *childTidList = nullptr; + if (pid == 0) { + childTidList = new int[1]; + childTidList[0] = 0; + *numChild = 1; + return childTidList; + } char dirPath[PATH_LEN]; if (snprintf(dirPath, sizeof(dirPath), "/proc/%d/task", pid) < 0) { return nullptr; diff --git a/util/read_reg.cpp b/util/read_reg.cpp new file mode 100644 index 0000000000000000000000000000000000000000..abf550c57975218c25287ee9ed7e3d4e584c8d1c --- /dev/null +++ b/util/read_reg.cpp @@ -0,0 +1,145 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * libkperf licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: yupan + * Create: 2025-07-31 + * Description: Provide some funtions for reading register and counting + ******************************************************************************/ +#include "read_reg.h" + +uint64_t ReadPmccntr() +{ + uint64_t val; + asm volatile("mrs %0, pmccntr_el0" : "=r"(val)); + return val; +} + +uint64_t ReadPmevcntr(int idx) +{ + uint64_t val = 0; + switch (idx) { + case 0: + asm volatile("mrs %0, pmevcntr0_el0" : "=r"(val)); + break; + case 1: + asm volatile("mrs %0, pmevcntr1_el0" : "=r"(val)); + break; + case 2: + asm volatile("mrs %0, pmevcntr2_el0" : "=r"(val)); + break; + case 3: + asm volatile("mrs %0, pmevcntr3_el0" : "=r"(val)); + break; + case 4: + asm volatile("mrs %0, pmevcntr4_el0" : "=r"(val)); + break; + case 5: + asm volatile("mrs %0, pmevcntr5_el0" : "=r"(val)); + break; + case 6: + asm volatile("mrs %0, pmevcntr6_el0" : "=r"(val)); + break; + case 7: + asm volatile("mrs %0, pmevcntr7_el0" : "=r"(val)); + break; + case 8: + asm volatile("mrs %0, pmevcntr8_el0" : "=r"(val)); + break; + case 9: + asm volatile("mrs %0, pmevcntr9_el0" : "=r"(val)); + break; + case 10: + asm volatile("mrs %0, pmevcntr10_el0" : "=r"(val)); + break; + case 11: + asm volatile("mrs %0, pmevcntr11_el0" : "=r"(val)); + break; + case 12: + asm volatile("mrs %0, pmevcntr12_el0" : "=r"(val)); + break; + case 13: + asm volatile("mrs %0, pmevcntr13_el0" : "=r"(val)); + break; + case 14: + asm volatile("mrs %0, pmevcntr14_el0" : "=r"(val)); + break; + case 15: + asm volatile("mrs %0, pmevcntr15_el0" : "=r"(val)); + break; + case 16: + asm volatile("mrs %0, pmevcntr16_el0" : "=r"(val)); + break; + case 17: + asm volatile("mrs %0, pmevcntr17_el0" : "=r"(val)); + break; + case 18: + asm volatile("mrs %0, pmevcntr18_el0" : "=r"(val)); + break; + case 19: + asm volatile("mrs %0, pmevcntr19_el0" : "=r"(val)); + break; + case 20: + asm volatile("mrs %0, pmevcntr20_el0" : "=r"(val)); + break; + case 21: + asm volatile("mrs %0, pmevcntr21_el0" : "=r"(val)); + break; + case 22: + asm volatile("mrs %0, pmevcntr22_el0" : "=r"(val)); + break; + case 23: + asm volatile("mrs %0, pmevcntr23_el0" : "=r"(val)); + break; + case 24: + asm volatile("mrs %0, pmevcntr24_el0" : "=r"(val)); + break; + case 25: + asm volatile("mrs %0, pmevcntr25_el0" : "=r"(val)); + break; + case 26: + asm volatile("mrs %0, pmevcntr26_el0" : "=r"(val)); + break; + case 27: + asm volatile("mrs %0, pmevcntr27_el0" : "=r"(val)); + break; + case 28: + asm volatile("mrs %0, pmevcntr28_el0" : "=r"(val)); + break; + case 29: + asm volatile("mrs %0, pmevcntr29_el0" : "=r"(val)); + break; + case 30: + asm volatile("mrs %0, pmevcntr30_el0" : "=r"(val)); + break; + default: + break; + } + + return val; +} + +uint64_t ReadPerfCounter(int idx) +{ + if (idx >= 0 && idx <= 30) { + return ReadPmevcntr(idx); + } + + if (idx == 31) { + return ReadPmccntr(); + } + return 0; +} + +uint64_t ReadCntvct() +{ + uint64_t val; + asm volatile("mrs %0, cntvct_el0" : "=r"(val)); + return val; +} \ No newline at end of file diff --git a/util/read_reg.h b/util/read_reg.h new file mode 100644 index 0000000000000000000000000000000000000000..87de108aaa478e2c658831c3ed9b015e5dec650a --- /dev/null +++ b/util/read_reg.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * libkperf licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: yupan + * Create: 2025-07-31 + * Description: Provide some funtions for reading register and counting + ******************************************************************************/ +#ifndef READ_REG_H +#define READ_REG_H + +#include + +uint64_t ReadPmccntr(); +uint64_t ReadPmevcntr(int idx); +uint64_t ReadPerfCounter(int idx); +uint64_t ReadCntvct(); + +#ifndef ReadTimestamp +static uint64_t ReadTimestamp(void) +{ + return ReadCntvct(); +} +#endif + +#ifndef Barrier +static inline void Barrier() +{ + asm volatile("" : : : "memory"); +} +#endif + +#ifndef MulU32U32 +static inline uint64_t MulU32U32(uint32_t a, uint32_t b) +{ + return (uint64_t)a * b; +} +#endif + +#ifndef MulU64U32Shr +static inline uint64_t MulU64U32Shr(uint64_t a, uint32_t mul, unsigned int shift) +{ + uint32_t ah = a >> 32; + uint32_t al = a; + uint64_t ret; + + ret = MulU32U32(al, mul) >> shift; + if (ah) { + ret += MulU32U32(ah, mul) << (32 - shift); + } + return ret; +} +#endif +#endif \ No newline at end of file