diff --git a/include/pcerrc.h b/include/pcerrc.h index 909ae4e9d8529d50677965d2f09e08a85f1432df..d8a11097e605e1fc9c2e8311fa9e1949a4e8d5c6 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -110,6 +110,7 @@ extern "C" { #define LIBPERF_ERR_NOT_SUPPORT_METRIC 1066 #define LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD 1067 #define LIBPERF_ERR_PMU_DATA_NO_FOUND 1068 +#define LIBPERF_ERR_INVALID_CGROUP_LIST 1069 #define UNKNOWN_ERROR 9999 diff --git a/include/pmu.h b/include/pmu.h index 0873c0caf5600dda429f436f3c8bcc7bed69d90b..22ace8f857a72e89a0f8dfe652ff29f88cf74f55 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -165,6 +165,11 @@ struct PmuAttr { unsigned includeNewFork : 1; // if the filtering mode is set, the branch_sample_stack data is collectd in sampling mode.By default,the filtering mode dose not take effect. unsigned long branchSampleFilter; + // cgroup name + // if not use cgroup function, this field will be nullptr. + // if use cgroup function. use the cgroup name in the cgroupNamelist to apply all events in the Event list + char** cgroupNameList; + int numCgroup; }; enum PmuTraceType { @@ -254,6 +259,7 @@ struct PmuData { double countPercent; // event count Percent. when count = 0, countPercent = -1; Only available for Counting. struct PmuDataExt *ext; // extension. Only available for Spe. struct SampleRawData *rawData; // trace pointer collect data. + const char* cgroupName; // trace data from which cgroup }; struct PmuTraceData { diff --git a/pmu/evt.h b/pmu/evt.h index ca48e4ed7666ba761b46b492e06ce6e81a14daba..9edcfd02855d861b16c0837fb85774614d9026db 100644 --- a/pmu/evt.h +++ b/pmu/evt.h @@ -67,6 +67,10 @@ public: return pid; } + int GetCgroupFd() const { + return evt->cgroupFd; + } + virtual bool IsMainPid() const { if (procMap.find(pid) != procMap.end()) { diff --git a/pmu/perf_counter.cpp b/pmu/perf_counter.cpp index 9b2db6dd24fbe3bf34ef878d0f51d0163b9feebd..de25a70815da5ab637a5f0581b1ec47ff64f812c 100644 --- a/pmu/perf_counter.cpp +++ b/pmu/perf_counter.cpp @@ -163,6 +163,9 @@ int KUNPENG_PMU::PerfCounter::CountValueToData(const __u64 value, const __u64 ti if (findProc != procMap.end()) { current.pid = findProc->second->pid; } + if(this->evt->cgroupName.size() != 0) { + current.cgroupName = this->evt->cgroupName.c_str(); + } return SUCCESS; } @@ -194,6 +197,13 @@ int KUNPENG_PMU::PerfCounter::MapPerfAttr(const bool groupEnable, const int grou attr.disabled = 1; attr.inherit = 1; + // support cgroup feature + unsigned flags = 0; + if (this->GetCgroupFd() != -1) { + flags = PERF_FLAG_PID_CGROUP | PERF_FLAG_FD_CLOEXEC; + this->pid = this->GetCgroupFd(); + } + /** * For now we set the format id bit to implement grouping logic in the future */ @@ -204,6 +214,7 @@ int KUNPENG_PMU::PerfCounter::MapPerfAttr(const bool groupEnable, const int grou * and any child events are initialized with disabled bit set to 0. Despite disabled bit being set to 0, * the child events will not start counting until the group leader is enabled. */ + if (groupFd != -1) { attr.disabled = 0; groupStatus = GroupStatus::GROUP_MEMBER; @@ -211,17 +222,17 @@ int KUNPENG_PMU::PerfCounter::MapPerfAttr(const bool groupEnable, const int grou groupStatus = GroupStatus::GROUP_LEADER; } attr.read_format |= PERF_FORMAT_GROUP; - this->fd = PerfEventOpen(&attr, this->pid, this->cpu, groupFd, 0); + this->fd = PerfEventOpen(&attr, this->pid, this->cpu, groupFd, flags); } else { #ifdef IS_X86 if (this->evt->pmuType == KUNPENG_PMU::UNCORE_TYPE && !StartWith(this->evt->name, "cpu/")) { - this->fd = PerfEventOpen(&attr, -1, this->cpu, groupFd, 0); + this->fd = PerfEventOpen(&attr, -1, this->cpu, groupFd, flags); #else if (this->evt->pmuType == KUNPENG_PMU::UNCORE_TYPE && !StartWith(this->evt->name, "armv8_")) { this->fd = PerfEventOpen(&attr, -1, this->cpu, groupFd, 0); #endif } else { - this->fd = PerfEventOpen(&attr, this->pid, this->cpu, groupFd, 0); + this->fd = PerfEventOpen(&attr, this->pid, this->cpu, groupFd, flags); } groupStatus = GroupStatus::NO_GROUP; } diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index 30de9ef1bdfb717340b7ba300964c85bfec7a49e..28a72a3af2f051ecea103349a237b92ea232694e 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -168,6 +168,26 @@ static int CheckBranchSampleFilter(const unsigned long& branchSampleFilter, enum return SUCCESS; } +static int CheckCgroupNameList(unsigned numCgroup, char** cgroupName) +{ + if (numCgroup > 0 && cgroupName == nullptr) { + New(LIBPERF_ERR_INVALID_CGROUP_LIST, "Invalid cgroup name list: numcgroup is greater than 0, but cgroupName is null"); + return LIBPERF_ERR_INVALID_CGROUP_LIST; + } + + for (unsigned i = 0; i < numCgroup; i++) { + std::string cgroupPath = "/sys/fs/cgroup/perf_event/" + string(cgroupName[i]); + int cgroupFd = open(cgroupPath.c_str(), O_RDONLY); + if (cgroupFd < 0) { + New(LIBPERF_ERR_OPEN_INVALID_FILE, "open " + cgroupPath + " failed."); + return LIBPERF_ERR_OPEN_INVALID_FILE; + } + close(cgroupFd); + } + + return SUCCESS; +} + static int CheckCollectTypeConfig(enum PmuTaskType collectType, struct PmuAttr *attr) { if (collectType < 0 || collectType >= MAX_TASK_TYPE) { @@ -244,6 +264,12 @@ static int CheckAttr(enum PmuTaskType collectType, struct PmuAttr *attr) return err; } + err = CheckCgroupNameList(attr->numCgroup, attr->cgroupNameList); + if (err != SUCCESS) { + New(err); + return err; + } + return SUCCESS; } @@ -831,7 +857,17 @@ static void PrepareCpuList(PmuAttr *attr, PmuTaskAttr *taskParam, PmuEvt* pmuEvt } } -static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *attr, const char* evtName, const int groupId) +int GetCgroupFd(std::string& cgroupName) { + std::string cgroupPath = "/sys/fs/cgroup/perf_event/" + cgroupName; + int cgroupFd = open(cgroupPath.c_str(), O_RDONLY); + if (cgroupFd < 0) { + New(LIBPERF_ERR_OPEN_INVALID_FILE, "open " + cgroupPath + " failed."); + return -1; + } + return cgroupFd; +} + +static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *attr, const char* evtName, const int groupId, const char* cgroupName, int cgroupFd) { unique_ptr taskParam(CreateNode(), PmuTaskAttrFree); /** @@ -842,6 +878,7 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att for (int i = 0; i < attr->numPid; i++) { taskParam->pidList[i] = attr->pidList[i]; } + PmuEvt* pmuEvt = nullptr; if (collectType == SPE_SAMPLING) { pmuEvt = PfmGetSpeEvent(attr->dataFilter, attr->evFilter, attr->minLatency, collectType); @@ -878,23 +915,61 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att taskParam->pmuEvt->callStack = attr->callStack; taskParam->pmuEvt->blockedSample = attr->blockedSample; taskParam->pmuEvt->includeNewFork = attr->includeNewFork; + taskParam->pmuEvt->cgroupFd = cgroupFd; + if (cgroupName != nullptr) { + taskParam->pmuEvt->cgroupName = cgroupName; + } + return taskParam.release(); } +bool InitCgroupFds(struct PmuAttr *attr, std::unordered_map& cgroupFds) { + for (int i = 0; i < attr->numCgroup; ++i) { + std::string cgroupName = attr->cgroupNameList[i]; + int fd = GetCgroupFd (cgroupName); + if (fd == -1) { + for (auto iter = cgroupFds.begin(); iter != cgroupFds.end(); iter++) { + close(iter->second); + } + return false; + } + cgroupFds[cgroupName] = fd; + } + return true; +} + struct PmuTaskAttr* AssignPmuTaskParam(enum PmuTaskType collectType, struct PmuAttr *attr) { struct PmuTaskAttr* taskParam = nullptr; if (collectType == SPE_SAMPLING) { // evtList is nullptr, cannot loop over evtList. - taskParam = AssignTaskParam(collectType, attr, nullptr, 0); + taskParam = AssignTaskParam(collectType, attr, nullptr, 0, nullptr, -1); return taskParam; } - for (int i = 0; i < attr->numEvt; i++) { - struct PmuTaskAttr* current = AssignTaskParam(collectType, attr, attr->evtList[i], attr->evtAttr[i].groupId); - if (current == nullptr) { + if (attr->numCgroup > 0) { + std::unordered_map cgroupFds; + if (!InitCgroupFds(attr, cgroupFds)) { return nullptr; } - AddTail(&taskParam, ¤t); + + for (int i = 0; i < attr->numCgroup; ++i) { + std::string cgroupName(attr->cgroupNameList[i]); + for (int j = 0; j < attr->numEvt; ++j) { + struct PmuTaskAttr* current = AssignTaskParam(collectType, attr, attr->evtList[j], attr->evtAttr[j].groupId, cgroupName.c_str(), cgroupFds[cgroupName]); + if (current == nullptr) { + return nullptr; + } + AddTail(&taskParam, ¤t); + } + } + } else { + for (int i = 0; i < attr->numEvt; ++i) { + struct PmuTaskAttr* current = AssignTaskParam(collectType, attr, attr->evtList[i], attr->evtAttr[i].groupId, nullptr, -1); + if (current == nullptr) { + return nullptr; + } + AddTail(&taskParam, ¤t); + } } return taskParam; } diff --git a/pmu/pmu_event.h b/pmu/pmu_event.h index 6fc6a72ddf6a71ba78ead681c08b6b86bc2906dd..6bffab302ed25c534e4348827d87047bd61a8ca3 100644 --- a/pmu/pmu_event.h +++ b/pmu/pmu_event.h @@ -49,6 +49,8 @@ struct PmuEvt { }; unsigned useFreq : 1; unsigned includeNewFork : 1; // count new fork tid + int cgroupFd; + std::string cgroupName; }; namespace KUNPENG_PMU { diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index cb78a80f1e896f4d64f972ddf0b08dd0f9834f57..1f96932a23998381640ace9575cd339d7f859c0b 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -82,9 +82,14 @@ int KUNPENG_PMU::PerfSampler::MapPerfAttr(const bool groupEnable, const int grou if (groupEnable) { attr.pinned = 0; attr.disabled = 0; - } + } + unsigned flags = 0; + if (this->GetCgroupFd() != -1) { + flags = PERF_FLAG_PID_CGROUP | PERF_FLAG_FD_CLOEXEC; + this->pid = this->GetCgroupFd(); + } - this->fd = PerfEventOpen(&attr, this->pid, this->cpu, groupFd, 0); + this->fd = PerfEventOpen(&attr, this->pid, this->cpu, groupFd, flags); DBG_PRINT("pid: %d type: %d cpu: %d config: %X myfd: %d groupfd: %d\n", this->pid, attr.type, cpu, attr.config, this->fd, groupFd); if (__glibc_unlikely(this->fd < 0)) { return MapErrno(errno); @@ -247,6 +252,9 @@ void KUNPENG_PMU::PerfSampler::RawSampleProcess( TraceParser::ParserRawFormatData(current, sample, event, this->evt->name); } ParseBranchSampleData(current, sample, event, extPool); + if (this->evt->cgroupName.size() != 0) { + current->cgroupName = this->evt->cgroupName.c_str(); + } } void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vector &sampleIps, diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index 8cead80ba3372eefc11193fd4c3d2aebe04aad35..2de8a3ae62bfbff5204a23a01e008cbc5a01ee43 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -202,7 +202,9 @@ class PmuAttr(object): evFilter=0, minLatency=0, includeNewFork=False, - branchSampleFilter=0): + branchSampleFilter=0, + cgroupNameList=None): + self.__c_pmu_attr = CtypesPmuAttr( evtList=evtList, pidList=pidList, @@ -220,6 +222,7 @@ class PmuAttr(object): minLatency=minLatency, includeNewFork=includeNewFork, branchSampleFilter=branchSampleFilter, + cgroupNameList=cgroupNameList ) @property @@ -244,6 +247,24 @@ class PmuAttr(object): self.c_pmu_attr.evtList = None self.c_pmu_attr.numEvt = ctypes.c_uint(0) + @property + def numCgroup(self): + return self.c_pmu_attr.numCgroup + + @property + def cgroupNameList(self): + return [self.c_pmu_attr.cgroupNameList[i].decode(UTF_8) for i in range(self.numCgroup)] + + @cgroupNameList.setter + def cgroupNameList(self, cgroupNameList): + if cgroupNameList: + numCgroup = len(cgroupNameList) + self.c_pmu_attr.cgroupNameList = (ctypes.c_char_p * numCgroup)(*[cgroupName.encode(UTF_8) for cgroupName in cgroupNameList]) + self.c_pmu_attr.numCgroup = ctypes.c_uint(numCgroup) + else: + self.c_pmu_attr.cgroupNameList = None + self.c_pmu_attr.numCgroup = ctypes.c_uint(0) + @property def numPid(self): return self.c_pmu_attr.numPid diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py index 5806a155e0a1aa6ae36e4a55da8c0dabc80c5f1a..86b68a7c42f20484fd15629d1509747722098538 100644 --- a/python/modules/kperf/perror.py +++ b/python/modules/kperf/perror.py @@ -109,6 +109,7 @@ class Error: LIBPERF_ERR_NOT_SUPPORT_METRIC = 1066 LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD = 1067 LIBPERF_ERR_PMU_DATA_NO_FOUND = 1068 + LIBPERF_ERR_INVALID_CGROUP_LIST = 1069 UNKNOWN_ERROR = 9999 diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index cce26c5ab94f3576d26f1a7eb10eccb1fd4d6f3c..3f4dfe46bd3a95aee2c69787c799f334a4925e30 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -268,7 +268,8 @@ class PmuAttr(_libkperf.PmuAttr): evFilter = 0, minLatency = 0, includeNewFork = False, - branchSampleFilter = 0): + branchSampleFilter = 0, + cgroupNameList = None): super(PmuAttr, self).__init__( evtList=evtList, pidList=pidList, @@ -286,6 +287,7 @@ class PmuAttr(_libkperf.PmuAttr): minLatency=minLatency, includeNewFork=includeNewFork, branchSampleFilter=branchSampleFilter, + cgroupNameList=cgroupNameList ) diff --git a/test/test_perf/test_api.cpp b/test/test_perf/test_api.cpp index edb431123cd77a6ae315e1768de39208fdb05b9f..929ff5e80308f69ced5296d360ea23f430f0c9d8 100644 --- a/test/test_perf/test_api.cpp +++ b/test/test_perf/test_api.cpp @@ -686,4 +686,103 @@ TEST_F(TestAPI, TestCpuFreqSampling) { PmuCpuFreqDetail* pDetail2 = PmuReadCpuFreqDetail(&cpuNum); ASSERT_EQ(cpuNum, MAX_CPU_NUM); PmuCloseCpuFreqSampling(); +} + +TEST_F(TestAPI, InvalidCgroupNameList) +{ + auto attr = GetPmuAttribute(); + attr.cgroupNameList = nullptr; + attr.numCgroup = 1; + auto pd = PmuOpen(SAMPLING, &attr); + ASSERT_EQ(pd, -1); +} + + +TEST_F(TestAPI, cgroupNameNotExist) +{ + auto attr = GetPmuAttribute(); + char* cgroupName[1]; + cgroupName[0] = (char*)"mygrouptest"; + attr.cgroupNameList = cgroupName; + attr.numCgroup = 1; + auto pd = PmuOpen(SAMPLING, &attr); + ASSERT_EQ(pd, -1); +} + +TEST_F(TestAPI, ValidCgroupName) +{ + const std::string cgroupPath= "/sys/fs/cgroup/perf_event/testcgroup"; + if (mkdir(cgroupPath.c_str(), 0755) != 0) { + std::cout << "make testcgroup failed" << std::endl; + return; + } + auto attr = GetPmuAttribute(); + char* cgroupName[1]; + cgroupName[0] = (char*)"testcgroup"; + attr.cgroupNameList = cgroupName; + attr.numCgroup = 1; + auto pd = PmuOpen(SAMPLING, &attr); + ASSERT_GT(pd, 0); + if (rmdir(cgroupPath.c_str()) != 0) { + std::cout << "remove testcgroup failed" << std::endl; + } +} + +TEST_F(TestAPI, cgroupNameSampleNormal) +{ + // 创建cgroup + const std::string cgroupPath= "/sys/fs/cgroup/perf_event/testcgroup"; + if (mkdir(cgroupPath.c_str(), 0755) != 0) { + std::cout << "make testcgroup failed" << std::endl; + return; + } + + // 向cgroup中写入进程pid + pid_t pid = getpid(); + std::stringstream ss; + ss << pid; + const std::string cgroupProc = cgroupPath + "/cgroup.procs"; + std::ofstream ofs(cgroupProc); + if (!ofs) { + std::cout << "open cgroup proc: " << cgroupProc << " failed " << std::endl; + if (rmdir(cgroupPath.c_str()) != 0) { + std::cout << "remove testcgroup failed" << std::endl; + } + return; + } + ofs << ss.str(); + + auto attr = GetPmuAttribute(); + char* cgroupName[1]; + cgroupName[0] = (char*)"testcgroup"; + attr.cgroupNameList = cgroupName; + attr.numCgroup = 1; + auto pd = PmuOpen(SAMPLING, &attr); + if (pd <= 0) { + if (rmdir(cgroupPath.c_str()) != 0) { + std::cout << "remove testcgroup failed" << std::endl; + } + } + + PmuEnable(pd); + PmuData* pmuData = nullptr; + int len = PmuRead(pd, &pmuData); + if (len == -1) { + std::cerr << "error msg:" << Perror() << std::endl; + if (rmdir(cgroupPath.c_str()) != 0) { + std::cout << "remove testcgroup failed" << std::endl; + } + return; + } + + PmuData data = pmuData[0]; + ASSERT_EQ(data.cgroupName, "testcgroup"); + ASSERT_GT(data.period, 0); + PmuDataFree(pmuData); + PmuDisable(pd); + PmuClose(pd); + + if (rmdir(cgroupPath.c_str()) != 0) { + std::cout << "remove testcgroup failed" << std::endl; + } } \ No newline at end of file