From 5c1e025f485b72bb4584d00f8454a23624b58526 Mon Sep 17 00:00:00 2001 From: wuying39 <921169248@qq.com> Date: Mon, 26 May 2025 16:35:13 +0800 Subject: [PATCH 01/15] llc_miss_ratio demo --- example/llc_miss_ratio.cpp | 346 +++++++++++++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 example/llc_miss_ratio.cpp diff --git a/example/llc_miss_ratio.cpp b/example/llc_miss_ratio.cpp new file mode 100644 index 0000000..5891f5d --- /dev/null +++ b/example/llc_miss_ratio.cpp @@ -0,0 +1,346 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2025. 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: + * Create: 2025-05-13 + * Description: Collection capability for ddrc and l3c + * Current capability: Top-N thread sort of l3c_cache_miss ratio + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pcerrc.h" +#include "pmu.h" +#include "symbol.h" + +static std::map numaTotalDDRC; //numa Id --> average ddrc bandwidth +static std::unordered_map numaToCpuCore; //numa Id --> cpu core ids +static std::unordered_map numaToCpuNumber; //numa Id --> length of cpu cores +static std::vector pidBoundCpus; //bounded cpus of designated pid +static unsigned numaNum = 0; //number of NUMAs + +const int FLOAT_PRECISION = 2; +const int TIME_UNIT_TRANS = 1000; + +uint64_t topNum = 0; +uint64_t duration = 0; +uint64_t period = 0; + +void totalDDRCBandwidth() +{ + PmuDeviceAttr devAttr[2]; + devAttr[0].metric = PMU_DDR_READ_BW; + devAttr[1].metric = PMU_DDR_WRITE_BW; + int pd = PmuDeviceOpen(devAttr, 2); + PmuEnable(pd); + sleep(1); + PmuData *oriData = nullptr; + int oriLen = PmuRead(pd, &oriData); + PmuDeviceData *devData = nullptr; + auto len = PmuGetDevMetric(oriData, oriLen, devAttr, 2, &devData); + std::unordered_map stats; + for (int i = 0; i < len; ++i) { + stats[devData[i].ddrNumaId] += devData[i].count / 1024 / 1024; + } + for (const auto &entry : stats) { + int id = entry.first; + double sum = entry.second; + numaTotalDDRC[id] = sum; + } + numaNum = numaTotalDDRC.size(); + DevDataFree(devData); + PmuDataFree(oriData); + PmuDisable(pd); +} + +// get numaId --> cpu core ids +void initNumaToCoreList() +{ + unsigned *coreList; + for (unsigned i = 0; i < numaNum; ++i) { + coreList = nullptr; + int len = PmuGetNumaCore(i, &coreList); + numaToCpuCore[i] = coreList; + numaToCpuNumber[i] = len; + } +} + +// parse the CPU core range in the format "0-255" or "0-3,5" +std::vector parseCpuRange(const std::string &rangeStr) +{ + std::vector cpus; + std::stringstream ss(rangeStr); + std::string part; + + while(getline(ss, part, ',')) { + size_t hyphen_pos = part.find("-"); + if (hyphen_pos != std::string::npos) { + int start = std::stoi(part.substr(0, hyphen_pos)); + int end = std::stoi(part.substr(hyphen_pos + 1)); + if (start > end) { + std::cerr << "Invalid CPU range: " << part << std::endl; + } + for (int i = start; i <= end; ++i) { + cpus.push_back(i); + } + } else { + cpus.push_back(std::stoi(part)); + } + } + + std::sort(cpus.begin(), cpus.end()); + cpus.erase(unique(cpus.begin(), cpus.end()), cpus.end()); + return cpus; +} + +// get cpu core of pid from /proc/[pid]/stat +std::string getCpuAffinityList(int pid) +{ + std::string path = "/proc/" + std::to_string(pid) + "/status"; + std::ifstream in(path); + if (!in.is_open()) { + std::cerr << "Not found: " << path << std::endl; + return ""; + } + std::string line; + const std::string targetKey = "Cpus_allowed_list:"; + while (getline(in, line)) { + if (line.find(targetKey) == 0) { + size_t pos = line.find("\t"); + if (pos == std::string::npos) + pos = targetKey.length(); + return line.substr(pos + 1); + } + } +} + +int getCpuCore(int pid) +{ + try { + std::string rangeStr = getCpuAffinityList(pid); + if (rangeStr == "") { + return -1; + } + pidBoundCpus = parseCpuRange(rangeStr); + } catch (const std::exception &e) { + std::cerr << "Error: " << e.what() << std::endl; + return 1; + } +} + +bool hasCommonCpu(const unsigned *cpuArray, size_t arraySize, const std::vector &cpuVector) +{ + if (cpuArray == nullptr || arraySize == 0 || cpuVector.empty()) { + return false; + } + + if (arraySize < cpuVector.size()) { + std::unordered_set arraySet(cpuArray, cpuArray + arraySize); + for (const auto &cpu : cpuVector) { + if (arraySet.count(cpu) > 0) { + return true; + } + } + } else { + std::unordered_set vecSet(cpuVector.begin(), cpuVector.end()); + for (size_t i = 0; i < arraySize; ++i) { + if (vecSet.count(cpuArray[i]) > 0) { + return true; + } + } + } + + return false; +} + +std::string GetL3CMissPercent(unsigned llc_miss, unsigned llc_cache) +{ + std::ostringstream oss; + double ratio = llc_cache != 0 ? static_cast(llc_miss) / llc_cache * 100.0 : 0.0; + oss << std::fixed << std::setprecision(FLOAT_PRECISION) << ratio; + return oss.str(); +} + + +void PrintHotSpotGraph(const std::unordered_map> tidData) +{ + std::vector>> sortedVec(tidData.begin(), tidData.end()); + std::sort(sortedVec.begin(), sortedVec.end(), [](const auto& a, const auto& b) { + double ratioA = (a.second.second == 0) ? 0.0 : static_cast(a.second.first) / a.second.second; + double ratioB = (b.second.second == 0) ? 0.0 : static_cast(b.second.first) / b.second.second; + return ratioA > ratioB; + }); + + std::cout << std::string(100, '=') << std::endl; + std::cout << std::string(100, '-') << std::endl; + std::cout << " " << std::setw(10) << " " << std::setw(20) << std::left << "Tid" << std::setw(20) << "llc_cache_miss" + << std::setw(20) << "llc_cache" << std::setw(20) << "llc_cache_miss_ratio" << std::endl; + std::cout << std::string(100, '-') << std::endl; + + size_t outputNum = std::min(topNum, tidData.size()); + for (int i = 0; i < outputNum; ++i) { + std::cout << " " << std::setw(10) << i << std::setw(20) << std::left << sortedVec[i].first << std::setw(20) + << sortedVec[i].second.first << std::setw(20) << sortedVec[i].second.second << std::setw(20) + << GetL3CMissPercent(sortedVec[i].second.first, sortedVec[i].second.second) + "%" << std::endl; + } + + std::cout << std::string(100, '_') << std::endl; +} + +int GetPmuDataHotspot(PmuData* pmuData, int pmuDataLen) +{ + if (pmuData == nullptr || pmuDataLen == 0) { + return SUCCESS; + } + + std::unordered_map> tidData; //tid --> (0x33, 0x32) + for (int i = 0; i < pmuDataLen; ++i) { + PmuData& data = pmuData[i]; + if (strcmp(data.evt, "r33") == 0) { + tidData[data.tid].first += data.count; + } + if (strcmp(data.evt, "r32") == 0) { + tidData[data.tid].second += data.count; + } + } + PrintHotSpotGraph(tidData); + return SUCCESS; +} + +void collectL3CMissRatio(int pid) { + char* evtList[2]; + evtList[0] = (char*)"r33"; + evtList[1] = (char*)"r32"; + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = 2; + attr.pidList = &pid; + attr.numPid = 1; + attr.cpuList = pidBoundCpus.data(); + attr.numCpu = pidBoundCpus.size(); + + int pd = PmuOpen(COUNTING, &attr); + if (pd == -1) { + std::cerr << "PmuOpen failed" << std::endl; + std::cerr << "error msg:" << Perror() << std::endl; + return; + } + + PmuEnable(pd); + int collectTimes = duration * TIME_UNIT_TRANS / period; + for (int i = 0; i < collectTimes; ++i) { + usleep(period * TIME_UNIT_TRANS); + PmuData* pmuData = nullptr; + int len = PmuRead(pd, &pmuData); + if (len == -1) { + std::cerr << "error msg:" << Perror() << std::endl; + return; + } + GetPmuDataHotspot(pmuData, len); + PmuDataFree(pmuData); + } + PmuDisable(pd); + PmuClose(pd); + return; +} + +// g++ -o llc_miss_ratio llc_miss_ratio.cpp -I ./output/include/ -L ./output/lib/ -lkperf -lsym +// export LD_LIBRARY_PATH=/XXX/libkperf/output/lib/:$LD_LIBRARY_PATH +void print_usage() { + std::cerr << "Usage: llc_miss_ratio \n"; + std::cerr << "--threshold : the collect threshold of total ddrc bandwidth, unit M/s\n"; + std::cerr << "--topNum : the top N thread of llc miss ratio collection\n"; + std::cerr << "--duration : the total collect time of llc_miss_ratio, unit s\n"; + std::cerr << "--period : the period of llc_miss_ratio collect, unit ms\n"; + std::cerr << " example: llc_miss_ratio 100 10 10 1000 \n"; +} + +int main(int argc, char** argv) +{ + if (argc < 5) { + print_usage(); + return 0; + } + double threshold = 0.0; + int pid = 0; + bool collectL3CMissFlag = false; + + try { + threshold = std::stod(argv[1]); + if (threshold <= 0) { + throw std::invalid_argument("threshold must be a positive number."); + } + + topNum = std::stod(argv[2]); + if (topNum <= 0) { + throw std::invalid_argument("TopNum must be a positive number."); + } + + duration = std::stod(argv[3]); + if (duration <= 0) { + throw std::invalid_argument("Duration must be a positive number."); + } + + period = std::stoi(argv[4]); + if (period <= 0) { + throw std::invalid_argument("Period must be a positive integer."); + } + + try { + pid = std::stoi(argv[5]); + } catch (const std::invalid_argument& e) { + std::cerr << "Not valid process id: " << e.what() << "\n"; + } + } catch (const std::exception& e) { + std::cerr << "Error parsing arguments: " << e.what() << "\n"; + print_usage(); + return EXIT_FAILURE; + } + + totalDDRCBandwidth(); + initNumaToCoreList(); + if(getCpuCore(pid) == -1) { + return EXIT_FAILURE; + } + + for (const auto &data : numaTotalDDRC) { + std::cout << "Numa ID: " << data.first << ", total bandwidth: " << data.second << "M/s"; + // bandwidth of numa greater than the threshold, check whether bounded cpus of pid correspond to this numa cores + if (data.second > threshold) { + auto cpuCoreList = numaToCpuCore[data.first]; + if (hasCommonCpu(cpuCoreList, numaToCpuNumber[data.first], pidBoundCpus)) { + std::cout << " --> exceed threshold, and the process is running on this numa"; + collectL3CMissFlag = true; + } else { + std::cout << " --> exceed threshold, the process is not running on this numa"; + } + } else { + std::cout << " --> not exceed threshold"; + } + std::cout << std::endl; + } + + if (collectL3CMissFlag) { + collectL3CMissRatio(pid); //begin to collect llc_miss and llc_cache event + } + + return 0; +} \ No newline at end of file -- Gitee From c910469963132fd37fc074bce1ae4d1456eca002 Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Mon, 26 May 2025 16:39:35 +0800 Subject: [PATCH 02/15] =?UTF-8?q?unknow=20error=20=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E8=AE=BE=E7=BD=AE=E4=BB=A5=E5=8F=8ApmuAppend?= =?UTF-8?q?Data=20=E6=9F=A5=E8=AF=A2=E5=88=A4=E6=96=AD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pmu/evt_list.cpp | 7 ++++++- pmu/pmu_list.cpp | 2 +- pmu/pmu_metric.cpp | 2 +- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp index 2af3e2f..bee7fa8 100644 --- a/pmu/evt_list.cpp +++ b/pmu/evt_list.cpp @@ -97,10 +97,10 @@ int KUNPENG_PMU::EvtList::Init(const bool groupEnable, const std::shared_ptrIsMainPid()) { continue; } + if (err == LIBPERF_ERR_INVALID_EVENT) { if (branchSampleFilter != KPERF_NO_BRANCH_SAMPLE) { pcerr::SetCustomErr(err, "Invalid event:" + perfEvt->GetEvtName() + ", PMU Hardware or event type doesn't support branch stack sampling"); @@ -108,6 +108,11 @@ int KUNPENG_PMU::EvtList::Init(const bool groupEnable, const std::shared_ptrGetEvtName() + ", " + std::string{strerror(errno)}); } } + + if (err == UNKNOWN_ERROR) { + pcerr::SetCustomErr(err, std::string{strerror(errno)}); + } + return err; } fdList.insert(perfEvt->GetFd()); diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index c0470c0..31cc35e 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -413,7 +413,7 @@ namespace KUNPENG_PMU { } auto findToData = userDataList.find(*toData); - if (findFromData == userDataList.end()) { + if (findToData == userDataList.end()) { return LIBPERF_ERR_INVALID_PMU_DATA; } // For non-null target data list, append source list to end of target vector. diff --git a/pmu/pmu_metric.cpp b/pmu/pmu_metric.cpp index 8fc5d12..d196c2f 100644 --- a/pmu/pmu_metric.cpp +++ b/pmu/pmu_metric.cpp @@ -1565,7 +1565,7 @@ int64_t PmuGetCpuFreq(unsigned core) cpuPath << SYS_CPU_INFO_PATH << core << "/cpufreq/scaling_cur_freq"; if (!ExistPath(cpuPath.str())) { - New(LIBPERF_ERR_CPUFREQ_NOT_CONFIG, "Kernel not config cpuFreq Or core exceed cpuNums. Not exist " + cpuPath.str()); + New(LIBPERF_ERR_CPUFREQ_NOT_CONFIG, "Kernel not config cpuFreq or core exceed cpuNums. Not exist " + cpuPath.str()); return -1; } std::string curFreqStr = ReadFileContent(cpuPath.str()); -- Gitee From 0056ebccc8f7fc27778cde93eced01c27a36b92b Mon Sep 17 00:00:00 2001 From: twwang <920347125@qq.com> Date: Fri, 23 May 2025 16:16:59 +0800 Subject: [PATCH 03/15] the logic of symbol parsing is optimized for NO_SYMBOL_RESOLVE --- include/pcerrc.h | 1 + include/pmu.h | 8 ++++++ pmu/pmu.cpp | 5 ++++ pmu/pmu_list.cpp | 39 +++++++++++++++++++++----- pmu/pmu_list.h | 1 + pmu/sampler.cpp | 26 ++++++++---------- pmu/spe.cpp | 4 +-- python/modules/kperf/perror.py | 1 + symbol/symbol_resolve.cpp | 50 +++++++++++++++------------------- 9 files changed, 84 insertions(+), 51 deletions(-) diff --git a/include/pcerrc.h b/include/pcerrc.h index 259befa..909ae4e 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -109,6 +109,7 @@ extern "C" { #define LIBPERF_ERR_INTERFACE_NOT_SUPPORT_X86 1065 #define LIBPERF_ERR_NOT_SUPPORT_METRIC 1066 #define LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD 1067 +#define LIBPERF_ERR_PMU_DATA_NO_FOUND 1068 #define UNKNOWN_ERROR 9999 diff --git a/include/pmu.h b/include/pmu.h index 5ec1726..9d5e81b 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -352,6 +352,14 @@ void PmuStop(int pd); */ int PmuRead(int pd, struct PmuData** pmuData); +/** +* @brief +* When symbol mode is SNO_SYMBOL_RESOLVE, you can use this resolve PmuData Symbol after PmuRead function +* @param pmuData the data from PmuRead +* @return 0 indicates resolve success, otherwise return error code +*/ +int ResolvePmuDataSymbol(struct PmuData* pmuData); + /** * @brief * Append data list to another data list <*toData>. diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index 1fd80ea..b8a7a87 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -769,6 +769,11 @@ int PmuRead(int pd, struct PmuData** pmuData) } } +int ResolvePmuDataSymbol(struct PmuData* pmuData) +{ + return PmuList::GetInstance()->ResolvePmuDataSymbol(pmuData); +} + void PmuClose(int pd) { SetWarn(SUCCESS); diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index 31cc35e..2e6c4c3 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -418,7 +418,9 @@ namespace KUNPENG_PMU { } // For non-null target data list, append source list to end of target vector. auto& dataVec = findToData->second.data; + auto& ipsVec = findToData->second.sampleIps; dataVec.insert(dataVec.end(), findFromData->second.data.begin(), findFromData->second.data.end()); + ipsVec.insert(ipsVec.end(), findFromData->second.sampleIps.begin(), findFromData->second.sampleIps.end()); len = dataVec.size(); if (*toData != dataVec.data()) { @@ -625,9 +627,6 @@ namespace KUNPENG_PMU { void PmuList::FillStackInfo(EventData& eventData) { auto symMode = symModeList[eventData.pd]; - if (symMode == NO_SYMBOL_RESOLVE) { - return; - } // Parse dwarf and elf info of each pid and get stack trace for each pmu data. for (size_t i = 0; i < eventData.data.size(); ++i) { auto& pmuData = eventData.data[i]; @@ -636,15 +635,44 @@ namespace KUNPENG_PMU { SymResolverRecordModuleNoDwarf(pmuData.pid); } else if (symMode == RESOLVE_ELF_DWARF) { SymResolverRecordModule(pmuData.pid); + } else if (symMode == NO_SYMBOL_RESOLVE) { + SymResolverRecordModule(pmuData.pid); + continue; } else { continue; } + if (pmuData.stack == nullptr) { pmuData.stack = StackToHash(pmuData.pid, ipsData.ips.data(), ipsData.ips.size()); } } } + int PmuList::ResolvePmuDataSymbol(struct PmuData* iPmuData) + { + if (iPmuData == nullptr) { + New(LIBPERF_ERR_INVALID_PMU_DATA, "ipmuData is nullptr"); + return LIBPERF_ERR_INVALID_PMU_DATA; + } + auto userData = userDataList.find(iPmuData); + if (userData == userDataList.end()) { + New(LIBPERF_ERR_PMU_DATA_NO_FOUND, "ipmuData isn't in userDataList"); + return LIBPERF_ERR_PMU_DATA_NO_FOUND; + } + + auto& eventData = userDataList[iPmuData]; + auto symMode = symModeList[eventData.pd]; + for (size_t i = 0; i < eventData.data.size(); ++i) { + auto& pmuData = eventData.data[i]; + auto& ipsData = eventData.sampleIps[i]; + if (pmuData.stack == nullptr) { + pmuData.stack = StackToHash(pmuData.pid, ipsData.ips.data(), ipsData.ips.size()); + } + } + New(SUCCESS); + return SUCCESS; + } + void PmuList::AggregateData(const std::vector& evData, std::vector& newEvData) { // Acccumulate stat data in previous PmuCollect for convenient use. @@ -1025,9 +1053,6 @@ namespace KUNPENG_PMU { int PmuList::InitSymbolRecordModule(const unsigned pd, PmuTaskAttr* taskParam) { SymbolMode symMode = GetSymbolMode(pd); - if (symMode == NO_SYMBOL_RESOLVE) { - return SUCCESS; - } if (taskParam->pmuEvt->collectType == COUNTING) { return SUCCESS; @@ -1053,7 +1078,7 @@ namespace KUNPENG_PMU { } } - if (this->symModeList[pd] == RESOLVE_ELF_DWARF) { + if (this->symModeList[pd] == RESOLVE_ELF_DWARF || this->symModeList[pd] == NO_SYMBOL_RESOLVE) { for (const auto& pid: pidList) { int rt = SymResolverRecordModule(pid); if (rt != SUCCESS) { diff --git a/pmu/pmu_list.h b/pmu/pmu_list.h index b44ff55..523e2ad 100644 --- a/pmu/pmu_list.h +++ b/pmu/pmu_list.h @@ -76,6 +76,7 @@ public: void StoreSplitData(unsigned pd, std::pair& previousEventList, std::unordered_map& eventSplitMap); bool IsAllPidExit(const unsigned pd); + int ResolvePmuDataSymbol(struct PmuData* iPmuData); private: using ProcPtr = std::shared_ptr; diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index a12e709..cab1556 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -217,22 +217,20 @@ void KUNPENG_PMU::PerfSampler::RawSampleProcess( return; } KUNPENG_PMU::PerfRawSample *sample = (KUNPENG_PMU::PerfRawSample *)event->sample.array; - if (symMode != NO_SYMBOL_RESOLVE) { - // Copy ips from ring buffer and get stack info later. - if (evt->callStack == 0) { - int i = 0; - while (i < sample->nr && !IsValidIp(sample->ips[i])) { - i++; - } - if (i < sample->nr) { + // Copy ips from ring buffer and get stack info later. + if (evt->callStack == 0) { + int i = 0; + while (i < sample->nr && !IsValidIp(sample->ips[i])) { + i++; + } + if (i < sample->nr) { + ips->ips.push_back(sample->ips[i]); + } + } else { + for (int i = sample->nr - 1; i >= 0; --i) { + if (IsValidIp(sample->ips[i])) { ips->ips.push_back(sample->ips[i]); } - } else { - for (int i = sample->nr - 1; i >= 0; --i) { - if (IsValidIp(sample->ips[i])) { - ips->ips.push_back(sample->ips[i]); - } - } } } current->cpu = sample->cpu; diff --git a/pmu/spe.cpp b/pmu/spe.cpp index 0d1c7d9..2bc42c4 100644 --- a/pmu/spe.cpp +++ b/pmu/spe.cpp @@ -359,9 +359,9 @@ void Spe::CoreDummyData(struct SpeCoreContext *context, struct ContextSwitchData uint64_t off = dataTail % mpage->data_size; struct perf_event_header *header = (struct perf_event_header *)(ringBuf + off); - if (header->type == PERF_RECORD_MMAP && symbolMode != NO_SYMBOL_RESOLVE) { + if (header->type == PERF_RECORD_MMAP) { struct PerfRecordMmap *sample = (struct PerfRecordMmap *)header; - if (symbolMode == RESOLVE_ELF_DWARF) { + if (symbolMode == RESOLVE_ELF_DWARF || symbolMode == NO_SYMBOL_RESOLVE) { int ret = SymResolverUpdateModule(sample->tid, sample->filename, sample->addr); if (ret != SUCCESS) { // if the module fails to be updated, a warning is recorded to overwrite the failure error code. diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py index 4ba96b2..20776de 100644 --- a/python/modules/kperf/perror.py +++ b/python/modules/kperf/perror.py @@ -108,6 +108,7 @@ class Error: LIBPERF_ERR_INTERFACE_NOT_SUPPORT_X86 =1065 LIBPERF_ERR_NOT_SUPPORT_METRIC = 1066 LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD = 1067 + LIBPERF_ERR_PMU_DATA_NO_FOUND = 1068 UNKNOWN_ERROR = 9999 diff --git a/symbol/symbol_resolve.cpp b/symbol/symbol_resolve.cpp index 46f6ad5..2f971f4 100644 --- a/symbol/symbol_resolve.cpp +++ b/symbol/symbol_resolve.cpp @@ -21,6 +21,7 @@ #include #include #include +#include #include "name_resolve.h" #include "pcerr.h" #include "symbol_resolve.h" @@ -70,16 +71,15 @@ namespace { flag = false; } - static inline bool CheckIfFile(std::string mapline) + static inline bool CheckIfFile(const std::string& mapline) { - return (!((mapline.find(HUGEPAGE) != std::string::npos) || (mapline.find(DEV_ZERO) != std::string::npos) || - (mapline.find(ANON) != std::string::npos) || (mapline.find(STACK) != std::string::npos) || - (mapline.find(SOCKET) != std::string::npos) || (mapline.find(VSYSCALL) != std::string::npos) || - (mapline.find(HEAP) != std::string::npos) || (mapline.find(VDSO) != std::string::npos) || - (mapline.find(SYSV) != std::string::npos) || (mapline.find(VVAR) != std::string::npos)) && - (mapline.find(R_XP) != std::string::npos)) - ? true - : false; + const std::vector patterns = {HUGEPAGE, DEV_ZERO, ANON, STACK, SOCKET, VSYSCALL, HEAP ,VDSO, SYSV, VVAR}; + for (const auto& pattern :patterns) { + if (mapline.find(pattern) != std::string::npos) { + return false; + } + } + return mapline.find(R_XP) != std::string::npos; } static inline char* InitChar(int len) @@ -398,7 +398,7 @@ bool MyElf::IsExecFile() void MyElf::Emplace(unsigned long addr, const ELF_SYM& elfSym) { - this->symTab.insert({addr, elfSym}); + this->symTab.emplace(addr, elfSym); } ELF_SYM* MyElf::FindSymbol(unsigned long addr) @@ -548,15 +548,11 @@ int SymbolResolve::RecordModule(int pid, RecordModuleType recordModuleType) moduleSafeHandler.releaseLock(pid); return 0; } - char mapFile[MAP_LEN]; - if (snprintf(mapFile, MAP_LEN, "/proc/%d/maps", pid) < 0) { - moduleSafeHandler.releaseLock(pid); - return LIBSYM_ERR_SNPRINF_OPERATE_FAILED; - } + std::string mapFile = "/proc/" + std::to_string(pid) + "/maps"; std::ifstream file(mapFile); if (!file.is_open()) { pcerr::New(LIBSYM_ERR_OPEN_FILE_FAILED, - "libsym can't open file named " + std::string{mapFile} + " because of " + std::string{strerror(errno)}); + "libsym can't open file named " + mapFile + " because of " + std::string{strerror(errno)}); moduleSafeHandler.releaseLock(pid); return LIBSYM_ERR_OPEN_FILE_FAILED; } @@ -588,15 +584,11 @@ int SymbolResolve::UpdateModule(int pid, RecordModuleType recordModuleType) return SUCCESS; } // Get memory maps of pid. - char mapFile[MAP_LEN]; - if (snprintf(mapFile, MAP_LEN, "/proc/%d/maps", pid) < 0) { - moduleSafeHandler.releaseLock(pid); - return LIBSYM_ERR_SNPRINF_OPERATE_FAILED; - } + std::string mapFile = "/proc/" + std::to_string(pid) + "/maps"; std::ifstream file(mapFile); if (!file.is_open()) { pcerr::New(LIBSYM_ERR_OPEN_FILE_FAILED, - "libsym can't open file named " + std::string{mapFile} + " because of " + std::string{strerror(errno)}); + "libsym can't open file named " + mapFile + " because of " + std::string{strerror(errno)}); moduleSafeHandler.releaseLock(pid); return LIBSYM_ERR_OPEN_FILE_FAILED; } @@ -618,8 +610,8 @@ int SymbolResolve::UpdateModule(int pid, RecordModuleType recordModuleType) this->RecordDwarf(item->moduleName.c_str()); } } - for (auto mod : diffModVec) { - oldModVec.push_back(mod); + for (auto& mod : diffModVec) { + oldModVec.emplace_back(mod); } pcerr::New(SUCCESS); moduleSafeHandler.releaseLock(pid); @@ -1176,11 +1168,13 @@ std::vector> SymbolResolve::FindDiffMaps( const std::vector>& newMaps) const { std::vector> diffMaps; + std::set oldStarts; + for (const auto& oldMod : oldMaps) { + oldStarts.insert(oldMod->start); + } for (auto newMod : newMaps) { - for (auto oldMod : oldMaps) { - if (newMod->start != oldMod->start) { - diffMaps.push_back(newMod); - } + if (oldStarts.find(newMod->start) == oldStarts.end()) { + diffMaps.emplace_back(newMod); } } -- Gitee From b955e04999b925a633de71fca9376ba4f7d0badc Mon Sep 17 00:00:00 2001 From: twwang <920347125@qq.com> Date: Thu, 29 May 2025 10:35:58 +0800 Subject: [PATCH 04/15] =?UTF-8?q?libkperf=20=E6=94=AF=E6=8C=81=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E9=9D=99=E6=80=81=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/pmu.h | 2 +- pmu/CMakeLists.txt | 3 +++ symbol/CMakeLists.txt | 3 +++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/include/pmu.h b/include/pmu.h index 9d5e81b..1063cdb 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -354,7 +354,7 @@ int PmuRead(int pd, struct PmuData** pmuData); /** * @brief -* When symbol mode is SNO_SYMBOL_RESOLVE, you can use this resolve PmuData Symbol after PmuRead function +* When symbol mode is NO_SYMBOL_RESOLVE, you can use this resolve PmuData Symbol after PmuRead function * @param pmuData the data from PmuRead * @return 0 indicates resolve success, otherwise return error code */ diff --git a/pmu/CMakeLists.txt b/pmu/CMakeLists.txt index c68bfe0..4af6e76 100644 --- a/pmu/CMakeLists.txt +++ b/pmu/CMakeLists.txt @@ -31,8 +31,11 @@ include_directories(${SYMBOL_FILE_DIR}) include_directories(${PMU_DECODER_DIR}) ADD_LIBRARY(kperf SHARED ${PMU_SRC} ${UTIL_SRC} ${PFM_SRC} ${PMU_DECODER_SRC}) +ADD_LIBRARY(kperf_static STATIC ${PMU_SRC} ${UTIL_SRC} ${PFM_SRC} ${PMU_DECODER_SRC}) +set_target_properties(kperf_static PROPERTIES OUTPUT_NAME "kperf") target_link_libraries(kperf numa sym) target_compile_options(kperf PRIVATE -fPIC) install(TARGETS kperf DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +install(TARGETS kperf_static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) file(GLOB HEADER_FILES ${PROJECT_TOP_DIR}/include/*.h) install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/symbol/CMakeLists.txt b/symbol/CMakeLists.txt index 920d59e..aaa8988 100644 --- a/symbol/CMakeLists.txt +++ b/symbol/CMakeLists.txt @@ -16,6 +16,9 @@ include_directories(${INCLUDE_DIR}) message(${THIRD_PARTY}/elfin-parser/elf) ADD_LIBRARY(sym SHARED ${SYMBOL_SRC}) +ADD_LIBRARY(sym_static STATIC ${SYMBOL_SRC}) +set_target_properties(sym_static PROPERTIES OUTPUT_NAME "sym") target_link_libraries(sym elf_static dwarf_static pthread) install(TARGETS sym DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) +install(TARGETS sym_static DESTINATION ${CMAKE_INSTALL_PREFIX}/lib) install(FILES ${SYMBOL_FILE_DIR}/symbol.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) -- Gitee From af4e73474e4a9077187b5026ab46581768ee0316 Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Thu, 29 May 2025 15:23:29 +0800 Subject: [PATCH 05/15] =?UTF-8?q?=E9=80=82=E9=85=8D=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=A1=E6=81=AF,=E5=BD=93=E6=97=A0=E6=9D=83=E9=99=90?= =?UTF-8?q?=E6=97=B6=E6=8A=9B=E5=87=BA=E5=AF=B9=E5=BA=94=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pmu/pmu.cpp | 33 +++++++++++++++++++++++++++++++++ pmu/pmu_event_list.cpp | 27 +++++---------------------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index b8a7a87..cba1713 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -830,6 +830,24 @@ static void PrepareCpuList(PmuAttr *attr, PmuTaskAttr *taskParam, PmuEvt* pmuEvt } } +static bool PerfEventSupported(__u64 type, __u64 config) +{ + perf_event_attr attr{}; + memset(&attr, 0, sizeof(attr)); + attr.size = sizeof(struct perf_event_attr); + attr.type = type; + attr.config = config; + attr.disabled = 1; + attr.inherit = 1; + attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; + int fd = KUNPENG_PMU::PerfEventOpen(&attr, -1, 0, -1, 0); + if (fd < 0) { + return false; + } + close(fd); + return true; +} + static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *attr, const char* evtName, const int group_id) { unique_ptr taskParam(CreateNode(), PmuTaskAttrFree); @@ -851,6 +869,9 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att } else { pmuEvt = GetPmuEvent(evtName, collectType); if (pmuEvt == nullptr) { + if (Perrorno() != SUCCESS) { + return nullptr; + } #ifdef IS_X86 New(LIBPERF_ERR_INVALID_EVENT, "Invalid event: " + string(evtName) + ";x86 just supports core event and raw event"); #else @@ -858,6 +879,18 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att #endif return nullptr; } + + if (!PerfEventSupported(pmuEvt->type, pmuEvt->config)) { + int err = MapErrno(errno); + if (err == LIBPERF_ERR_NO_PERMISSION) { + New(LIBPERF_ERR_NO_PERMISSION, "Current user does not have the permission to collect the event.Swtich to the root user and run the 'echo -1 > /proc/sys/kernel/perf_event_paranoid'"); + } else if(err == UNKNOWN_ERROR) { + New(UNKNOWN_ERROR, std::string{strerror(errno)}); + } else { + New(err); + } + return nullptr; + } } /** * Assign cpus to collect diff --git a/pmu/pmu_event_list.cpp b/pmu/pmu_event_list.cpp index ab83ffd..547ccb2 100644 --- a/pmu/pmu_event_list.cpp +++ b/pmu/pmu_event_list.cpp @@ -101,24 +101,6 @@ static void GetTraceSubFolder(const std::string& traceFolder, const string& devN closedir(dir); } -static bool PerfEventSupported(__u64 type, __u64 config) -{ - perf_event_attr attr{}; - memset(&attr, 0, sizeof(attr)); - attr.size = sizeof(struct perf_event_attr); - attr.type = type; - attr.config = config; - attr.disabled = 1; - attr.inherit = 1; - attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; - int fd = KUNPENG_PMU::PerfEventOpen(&attr, -1, 0, -1, 0); - if (fd < 0) { - return false; - } - close(fd); - return true; -} - const char** QueryCoreEvent(unsigned *numEvt) { if (!coreEventList.empty()) { @@ -128,9 +110,6 @@ const char** QueryCoreEvent(unsigned *numEvt) auto coreEventMap = KUNPENG_PMU::CORE_EVENT_MAP.at(GetCpuType()); for (auto& pair : coreEventMap) { auto eventName = pair.first; - if (!PerfEventSupported(pair.second.type, pair.second.config)) { - continue; - } char* eventNameCopy = new char[eventName.length() + 1]; strcpy(eventNameCopy, eventName.c_str()); coreEventList.emplace_back(eventNameCopy); @@ -203,6 +182,11 @@ const char** QueryTraceEvent(unsigned *numEvt) struct dirent *entry; const string &traceFolder = GetTraceEventDir(); if (traceFolder.empty()) { + if (errno == EACCES) { + New(LIBPERF_ERR_NO_PERMISSION, "no permission to access '/sys/kernel/tracing/events/' or '/sys/kernel/debug/tracing/events/'"); + } else { + New(LIBPERF_ERR_INVALID_EVENT, "can't find '/sys/kernel/tracing/events/' or '/sys/kernel/debug/tracing/events/'"); + } return traceEventList.data(); } DIR *dir = opendir(traceFolder.c_str()); @@ -282,7 +266,6 @@ const char** PmuEventList(enum PmuEventType eventType, unsigned *numEvt) New(LIBPERF_ERR_QUERY_EVENT_LIST_FAILED, "Query event failed."); return nullptr; } - New(SUCCESS); return eventList; } -- Gitee From 919f886fb118d5bd3c105f97b9746ea01776e0af Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Fri, 30 May 2025 10:47:23 +0800 Subject: [PATCH 06/15] =?UTF-8?q?go=E9=80=82=E9=85=8DResolvePmuDataSymbol?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go/src/libkperf/kperf/kperf.go | 25 +++++++++++++++++++ go/src/libkperf_test/libkperf_test.go | 36 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/go/src/libkperf/kperf/kperf.go b/go/src/libkperf/kperf/kperf.go index 99fb52d..b21f304 100644 --- a/go/src/libkperf/kperf/kperf.go +++ b/go/src/libkperf/kperf/kperf.go @@ -728,6 +728,31 @@ func PmuDumpData(dataVo PmuDataVo, filePath string, dumpDwf bool) error { } return nil } + +// When symbol mode is SNO_SYMBOL_RESOLVE, you can use this resolve PmuData Symbol after PmuRead function +// param PmuDataVo the data from PmuRead +// return nil indicates resolve success, otherwise return error code +func ResolvePmuDataSymbol(dataVo PmuDataVo) error { + err := C.ResolvePmuDataSymbol(dataVo.cData) + if int(err) != 0 { + return errors.New(C.GoString(C.Perror())) + } + dataLen := len(dataVo.GoData) + ptr := unsafe.Pointer(dataVo.cData) + slice := reflect.SliceHeader { + Data: uintptr(ptr), + Len: dataLen, + Cap: dataLen, + } + cPmuDatas := *(*[]C.struct_PmuData)(unsafe.Pointer(&slice)) + for i := 0; i < dataLen; i++ { + dataObj := cPmuDatas[i] + if dataObj.stack != nil { + dataVo.GoData[i].appendSymbols(dataObj) + } + } + return nil +} // Initialize the trace collection target // On success, a trace collect task id is returned which is the unique identity for the task diff --git a/go/src/libkperf_test/libkperf_test.go b/go/src/libkperf_test/libkperf_test.go index 28848fe..d06683a 100644 --- a/go/src/libkperf_test/libkperf_test.go +++ b/go/src/libkperf_test/libkperf_test.go @@ -287,3 +287,39 @@ func TestPmuGetCpuFreqDetail(t *testing.T) { kperf.PmuCloseCpuFreqSampling() } + +func TestResolvePmuDataSymbol(t *testing.T) { + attr := kperf.PmuAttr{EvtList:[]string{"cycles"}, CallStack:true, SampleRate: 1000, UseFreq:true} + fd, err := kperf.PmuOpen(kperf.SAMPLE, attr) + if err != nil { + t.Fatalf("kperf pmuopen sample failed, expect err is nil, but is %v", err) + } + + kperf.PmuEnable(fd) + time.Sleep(time.Second) + kperf.PmuDisable(fd) + + dataVo, err := kperf.PmuRead(fd) + if err != nil { + t.Fatalf("kperf pmuread failed, expect err is nil, but is %v", err) + } + + for _, o := range dataVo.GoData { + if len(o.Symbols) != 0 { + t.Fatalf("expect symbol data is empty, but is not") + } + } + + parseErr := kperf.ResolvePmuDataSymbol(dataVo) + if parseErr != nil { + t.Fatalf("kperf ResolvePmuDataSymbol failed, expect err is nil, but is %v", parseErr) + } + + for _, o := range dataVo.GoData { + if len(o.Symbols) == 0 { + t.Fatalf("expect symbol data is not empty, but is empty") + } + } + kperf.PmuDataFree(dataVo) + kperf.PmuClose(fd) +} \ No newline at end of file -- Gitee From 5670c811e5ca869441f0e31c1edcf56b0effc2ef Mon Sep 17 00:00:00 2001 From: twwang <920347125@qq.com> Date: Fri, 30 May 2025 10:16:28 +0800 Subject: [PATCH 07/15] =?UTF-8?q?=E6=8F=90=E4=BE=9BResolvePmuDataSymbol?= =?UTF-8?q?=E7=9A=84python=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/modules/_libkperf/Pmu.py | 10 ++++++++++ python/modules/kperf/pmu.py | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index c65413e..2cffe19 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -1619,6 +1619,15 @@ def PmuRead(pd: int) -> PmuData: c_data_len = c_PmuRead(c_pd, ctypes.byref(c_data_pointer)) return PmuData(c_data_pointer, c_data_len) +def ResolvePmuDataSymbol(pmuData: ctypes.POINTER(CtypesPmuData)) -> int: + """ + int ResolvePmuDataSymbol(struct PmuData* pmuData); + """ + c_ResolvePmuDataSymbol = kperf_so.ResolvePmuDataSymbol + c_ResolvePmuDataSymbol.argtypes = [ctypes.POINTER(CtypesPmuData)] + c_ResolvePmuDataSymbol.restype = ctypes.c_int + + return c_ResolvePmuDataSymbol(pmuData) def PmuAppendData(fromData: ctypes.POINTER(CtypesPmuData), toData: ctypes.POINTER(ctypes.POINTER(CtypesPmuData))) -> int: @@ -2086,4 +2095,5 @@ __all__ = [ 'PmuReadCpuFreqDetail', 'PmuCloseCpuFreqSampling', 'PmuCpuFreqDetail', + 'ResolvePmuDataSymbol' ] diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index 672a060..3d33b2e 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -390,6 +390,14 @@ def read(pd: int) -> PmuData: """ return _libkperf.PmuRead(pd) +def resolvePmuDataSymbol(pmuData: PmuData) -> PmuData: + """ + when kperf symbol mode is NO_SYMBOL_RESOLVE during PmuRead(), this function can be used to resolve stack symbols + :param: pmuData + :return: pmu data + """ + return _libkperf.ResolvePmuDataSymbol(pmuData.pointer()) + def stop(pd: int) -> None: """ @@ -595,4 +603,5 @@ __all__ = [ 'open_cpu_freq_sampling', 'close_cpu_freq_sampling', 'read_cpu_freq_detail', + 'resolvePmuDataSymbol' ] -- Gitee From d0e95053e57528eaa1e356e26f072757ba71e599 Mon Sep 17 00:00:00 2001 From: twwang <920347125@qq.com> Date: Fri, 30 May 2025 17:34:05 +0800 Subject: [PATCH 08/15] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=87=BD=E6=95=B0?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- python/modules/kperf/pmu.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index 3d33b2e..2f0a2b5 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -390,7 +390,7 @@ def read(pd: int) -> PmuData: """ return _libkperf.PmuRead(pd) -def resolvePmuDataSymbol(pmuData: PmuData) -> PmuData: +def resolvePmuDataSymbol(pmuData: PmuData) -> int: """ when kperf symbol mode is NO_SYMBOL_RESOLVE during PmuRead(), this function can be used to resolve stack symbols :param: pmuData -- Gitee From e0ab74ce6cc6fcb113fa9e3b1f5ad8defea6309f Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Fri, 30 May 2025 17:18:32 +0800 Subject: [PATCH 09/15] =?UTF-8?q?=E4=B8=8D=E7=9B=B4=E6=8E=A5=E6=9A=B4?= =?UTF-8?q?=E9=9C=B2symbol=E6=8E=A5=E5=8F=A3=E4=BA=A7=E7=94=9F=E7=9A=84?= =?UTF-8?q?=E5=BC=82=E5=B8=B8,=20=E8=80=8C=E4=BD=9C=E4=B8=BAwarning?= =?UTF-8?q?=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pmu/pmu_list.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index 2e6c4c3..d0a47c4 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -646,6 +646,12 @@ namespace KUNPENG_PMU { pmuData.stack = StackToHash(pmuData.pid, ipsData.ips.data(), ipsData.ips.size()); } } + //Exceptions generated by the symbol interface are not directly exposed and are processed as warnings. + int err = Perrorno(); + if (err < LIBPERF_ERR_NO_AVAIL_PD && err >= LIBSYM_ERR_BASE) { + pcerr::SetWarn(err, Perror()); + New(SUCCESS); + } } int PmuList::ResolvePmuDataSymbol(struct PmuData* iPmuData) -- Gitee From b36353dac490f85f10da33d8142e47d77729083c Mon Sep 17 00:00:00 2001 From: glx Date: Wed, 28 May 2025 11:18:29 +0800 Subject: [PATCH 10/15] Fix compile error --- util/common.h | 1 + 1 file changed, 1 insertion(+) diff --git a/util/common.h b/util/common.h index 1c3bcb7..77b8a9b 100644 --- a/util/common.h +++ b/util/common.h @@ -18,6 +18,7 @@ #include #include #include +#include #ifdef __x86_64__ #define IS_X86 1 -- Gitee From a89e0518bf60bbc468fc869ff24f0359f78f7d53 Mon Sep 17 00:00:00 2001 From: Galaxy Date: Wed, 4 Jun 2025 08:43:45 +0000 Subject: [PATCH 11/15] =?UTF-8?q?=E9=80=82=E9=85=8Done=20numa=20per=20sock?= =?UTF-8?q?et=E7=9A=84ddrc=E9=87=87=E9=9B=86=E5=8A=9F=E8=83=BD=20=E9=B2=B2?= =?UTF-8?q?=E9=B9=8F=E6=9E=B6=E6=9E=84=E4=B8=8B=EF=BC=8C=E5=AF=B9=E4=BA=8E?= =?UTF-8?q?one=20numa=20per=20socket=E7=9A=84=E5=9C=BA=E6=99=AF=EF=BC=8Cdd?= =?UTF-8?q?rc=20pmu=E8=AE=BE=E5=A4=87=E5=92=8Csocket=E7=9A=84=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E5=85=B3=E7=B3=BB=E4=B8=8D=E5=8F=98=EF=BC=8C=E4=BD=86?= =?UTF-8?q?=E6=98=AF=E5=92=8Cnuma=E7=9A=84=E6=98=A0=E5=B0=84=E5=85=B3?= =?UTF-8?q?=E7=B3=BB=E5=8F=91=E7=94=9F=E4=BA=86=E5=8F=98=E5=8C=96=EF=BC=9A?= =?UTF-8?q?=20hisi=5Fsccl3=5FddrcX=20->=20socket=200=20numa=200=20hisi=5Fs?= =?UTF-8?q?ccl1=5FddrcX=20->=20socket=200=20numa=200=20hisi=5Fsccl11=5Fddr?= =?UTF-8?q?cX=20->=20socket=201=20numa=201=20hisi=5Fsccl9=5FddrcX=20->=20s?= =?UTF-8?q?ocket=201=20numa=201=20=E6=89=80=E4=BB=A5=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E9=80=9A=E8=BF=87ddrcNumaId=E6=9D=A5=E5=88=A4=E6=96=ADpmu?= =?UTF-8?q?=E8=AE=BE=E5=A4=87=E5=92=8Cchannel=20id=E7=9A=84=E6=98=A0?= =?UTF-8?q?=E5=B0=84=E5=85=B3=E7=B3=BB=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 目前借助sccl id和ddrc id共同来决定channel id的映射: sccl 3 ddrc 0 -> socket 0 channel 0 sccl 3 ddrc 2 -> socket 0 channel 1 sccl 3 ddrc 3 -> socket 0 channel 2 sccl 3 ddrc 5 -> socket 0 channel 3 sccl 1 ddrc 0 -> socket 0 channel 4 sccl 1 ddrc 2 -> socket 0 channel 5 sccl 1 ddrc 3 -> socket 0 channel 6 sccl 1 ddrc 5 -> socket 0 channel 7 ... Signed-off-by: Galaxy --- pmu/pmu_metric.cpp | 60 ++++++++++++++++++++++++++++++---------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/pmu/pmu_metric.cpp b/pmu/pmu_metric.cpp index d196c2f..66c70c4 100644 --- a/pmu/pmu_metric.cpp +++ b/pmu/pmu_metric.cpp @@ -38,6 +38,7 @@ using namespace std; using namespace pcerr; +using IdxMap = unordered_map>; static unsigned maxCpuNum = 0; static vector coreArray; @@ -1071,10 +1072,33 @@ namespace KUNPENG_PMU { return SUCCESS; } - static unordered_map> DDRC_CHANNEL_MAP = { - {CHIP_TYPE::HIPA, {0, 1, 2, 3}}, - {CHIP_TYPE::HIPB, {0, 2, 3, 5}} + static IdxMap DDRC_CHANNEL_MAP_HIPA = { + {1, {{0, 0}, {1, 1}, {2, 2}, {3, 3}}}, + {3, {{0, 4}, {1, 5}, {2, 6}, {3, 7}}}, + {5, {{0, 0}, {1, 1}, {2, 2}, {3, 3}}}, + {7, {{0, 4}, {1, 5}, {2, 6}, {3, 7}}}, }; + static IdxMap DDRC_CHANNEL_MAP_HIPB = { + {3, {{0, 0}, {2, 1}, {3, 2}, {5, 3}}}, + {1, {{0, 4}, {2, 5}, {3, 6}, {5, 7}}}, + {11, {{0, 0}, {2, 1}, {3, 2}, {5, 3}}}, + {9, {{0, 4}, {2, 5}, {3, 6}, {5, 7}}}, + }; + + static unordered_map DDRC_CHANNEL_MAP = { + {HIPA, DDRC_CHANNEL_MAP_HIPA}, + {HIPB, DDRC_CHANNEL_MAP_HIPB}, + }; + + static int ParseDDRIdx(const string &devName, const string prefix) + { + size_t ddrcPos = devName.find(prefix); + size_t channelIndex = ddrcPos + prefix.length(); + string ddrcIndexStr = devName.substr(channelIndex); + size_t separatorPos = ddrcIndexStr.find("_"); + int ddrcIndex = separatorPos != string::npos ? stoi(ddrcIndexStr.substr(0, separatorPos)) : stoi(ddrcIndexStr); + return ddrcIndex; + } static bool getChannelId(const char *evt, const unsigned ddrNumaId, unsigned &channelId) { @@ -1084,28 +1108,22 @@ namespace KUNPENG_PMU { return false; } // ddrc channel index. eg: hisi_sccl3_ddrc3_1 --> 3_1 - string ddrcStr = "ddrc"; - size_t ddrcPos = devName.find(ddrcStr); - size_t channelIndex = ddrcPos + ddrcStr.length(); - string ddrcIndexStr = devName.substr(channelIndex); - // find index in DDRC_CHANNEL_MAP. eg: 3_1 --> 3, corresponds to channel 2 in HIPB - size_t separatorPos = ddrcIndexStr.find("_"); - int ddrcIndex = separatorPos != string::npos ? stoi(ddrcIndexStr.substr(0, separatorPos)) : stoi(ddrcIndexStr); + int ddrcIndex = ParseDDRIdx(devName, "ddrc"); + int scclIndex = ParseDDRIdx(devName, "sccl"); - unsigned channelAddNum = 0; - if((ddrNumaId & 1) == 1) { // channel id + 4 in sequence - channelAddNum = 4; - } CHIP_TYPE chipType = GetCpuType(); //get channel index if (DDRC_CHANNEL_MAP.find(chipType) == DDRC_CHANNEL_MAP.end()) { return false; } - auto ddrcChannelList = DDRC_CHANNEL_MAP[chipType]; - auto it = find(ddrcChannelList.begin(), ddrcChannelList.end(), ddrcIndex); - if (it != ddrcChannelList.end()) { - size_t index = distance(ddrcChannelList.begin(), it); - channelId = index + channelAddNum; - return true; + + auto &ddrcChannelList = DDRC_CHANNEL_MAP[chipType]; + auto ddrIdxMap = ddrcChannelList.find(scclIndex); + if (ddrIdxMap != ddrcChannelList.end()) { + auto channelIdx = ddrIdxMap->second.find(ddrcIndex); + if (channelIdx != ddrIdxMap->second.end()) { + channelId = channelIdx->second; + return true; + } } return false; } @@ -1136,7 +1154,7 @@ namespace KUNPENG_PMU { outData.mode = GetMetricMode(data.metric); outData.channelId = channelId; outData.ddrNumaId = data.ddrNumaId; - outData.socketId = data.ddrNumaId < 2 ? 0 : 1; // numa id 0-1 --> socket id 0; numa id 2-3 --> socket id 1 + outData.socketId = data.socketId; devDataByChannel[ddrDatakey] = outData; } else { findData->second.count += data.count; -- Gitee From 36a924ac4b434b31d5fec2d1f916eb2809ef829c Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Tue, 3 Jun 2025 18:12:02 +0800 Subject: [PATCH 12/15] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=96=B0=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E7=9A=84=E6=96=87=E6=A1=A3=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Go_API.md | 79 +++++++++++++++++++++++++++++++++++++++++++++- docs/Python_API.md | 51 ++++++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) diff --git a/docs/Go_API.md b/docs/Go_API.md index 7199142..fd0a346 100644 --- a/docs/Go_API.md +++ b/docs/Go_API.md @@ -438,7 +438,8 @@ func main() { ``` -### kperf.PmuGetCpuFreq +### kperf.PmuGetCpuFreq + func PmuGetCpuFreq(core uint) (int64, error) 查询当前系统指定core的实时CPU频率 * core cpu coreId @@ -457,4 +458,80 @@ func main() { } fmt.Printf("coreId %v freq is %v\n", coreId, freq) } +``` + +### kperf.PmuOpenCpuFreqSampling + +func PmuOpenCpuFreqSampling(period uint) (error) 开启cpu频率采集 + +### kperf.PmuCloseCpuFreqSampling + +func PmuCloseCpuFreqSampling() 关闭cpu频率采集 + +### kperf.PmuReadCpuFreqDetail + +func PmuReadCpuFreqDetail() ([]PmuCpuFreqDetail) 读取开启频率采集到读取时间内的cpu最大频率、最小频率以及平均频率 +```go +import "libkperf/kperf" +import "fmt" + +func main() { + err := kperf.PmuOpenCpuFreqSampling(100) + if err != nil { + fmt.Printf("kperf PmuOpenCpuFreqSampling failed, expect err is nil, but is %v", err) + } + + freqList := kperf.PmuReadCpuFreqDetail() + for _, v := range freqList { + fmt.Printf("cpuId=%v, minFreq=%d, maxFreq=%d, avgFreq=%d", v.CpuId, v.MinFreq, v.MaxFreq, v.AvgFreq) + } + + kperf.PmuCloseCpuFreqSampling() +} +``` + +### kperf.ResolvePmuDataSymbol + +func ResolvePmuDataSymbol(dataVo PmuDataVo) error 当SymbolMode不设置或者设置为0时,可通过该接口解析PmuRead返回的PmuData数据中的符号 +```go +import "libkperf/kperf" +import "fmt" + +func main() { + attr := kperf.PmuAttr{EvtList:[]string{"cycles"}, CallStack:true, SampleRate: 1000, UseFreq:true} + fd, err := kperf.PmuOpen(kperf.SAMPLE, attr) + if err != nil { + fmt.Printf("kperf pmuopen sample failed, expect err is nil, but is %v", err) + return + } + + kperf.PmuEnable(fd) + time.Sleep(time.Second) + kperf.PmuDisable(fd) + + dataVo, err := kperf.PmuRead(fd) + if err != nil { + fmt.Printf("kperf pmuread failed, expect err is nil, but is %v", err) + return + } + + for _, o := range dataVo.GoData { + if len(o.Symbols) != 0 { + fmt.Printf("expect symbol data is empty, but is not") + } + } + + parseErr := kperf.ResolvePmuDataSymbol(dataVo) + if parseErr != nil { + fmt.Printf("kperf ResolvePmuDataSymbol failed, expect err is nil, but is %v", parseErr) + } + + for _, o := range dataVo.GoData { + if len(o.Symbols) == 0 { + fmt.Printf("expect symbol data is not empty, but is empty") + } + } + kperf.PmuDataFree(dataVo) + kperf.PmuClose(fd) +} ``` \ No newline at end of file diff --git a/docs/Python_API.md b/docs/Python_API.md index de323e9..a0a1968 100644 --- a/docs/Python_API.md +++ b/docs/Python_API.md @@ -428,4 +428,55 @@ kperf.get_numa_core(numaId: int): 查询指定numaId下对应的core列表 # python代码示例 numaId = 1 numa_cores = kperf.get_numa_core(numaId) +``` + +### kperf.open_cpu_freq_sampling + +def open_cpu_freq_sampling(period: int) 开启cpu频率采集 + +### kperf.close_cpu_freq_sampling + +def close_cpu_freq_sampling() 关闭cpu频率采集 + +### kperf.read_cpu_freq_detail + +def read_cpu_freq_detail() -> CpuFreqDetail 读取开启频率采集到读取时间内的cpu最大频率、最小频率以及平均频率 +```python +#python代码示例 +err = kperf.open_cpu_freq_sampling(100) +if err != 0: + print(f"error number: {kperf.errorno()} error message: {kperf.error()}") + exit(1) +dataList = kperf.read_cpu_freq_detail() +for item in dataList.iter: + print(f"cpuId={item.cpuId} minFreq={item.minFreq} maxFreq={item.maxFreq} avgFreq={item.avgFreq}") + +kperf.close_cpu_freq_sampling() +``` + +### kperf.resolvePmuDataSymbol + +def resolvePmuDataSymbol(pmuData: PmuData) -> int: 当SymbolMode不设置或者设置为0时,可通过该接口解析read返回的PmuData数据中的符号 +```python +#python代码示例 +event_name = "cycles" +pmu_attr = kperf.PmuAttr( + evtList=[event_name], + sampleRate=1000, + callStack=True, + useFreq=True, +) +fd = kperf.open(kperf.PmuTaskType.SAMPLING, pmu_attr) +if fd == -1: + print(f"error number: {kperf.errorno()} error message: {kperf.error()}") + exit(1) +kperf.enable(fd) +time.sleep(1) +kperf.disable(fd) +pmu_data = kperf.read(fd) +err = kperf.resolvePmuDataSymbol(pmu_data) +if err != 0: + print(f"error number: {kperf.errorno()} error message: {kperf.error()}") + exit(1) +kperf.close(fd) ``` \ No newline at end of file -- Gitee From 115ae38afc16c83a9063962c79e69d19614e91f9 Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Thu, 5 Jun 2025 09:41:26 +0800 Subject: [PATCH 13/15] =?UTF-8?q?includeNewFork=3D1=E6=97=B6,=E6=AE=B5?= =?UTF-8?q?=E9=94=99=E8=AF=AF=E5=BC=82=E5=B8=B8=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pmu/dummy_event.cpp | 6 +++-- pmu/dummy_event.h | 2 +- pmu/evt_list.cpp | 56 ++++++++++++++++++++++++++++++++------------- pmu/pmu.cpp | 31 +------------------------ pmu/pmu_list.cpp | 2 +- 5 files changed, 47 insertions(+), 50 deletions(-) diff --git a/pmu/dummy_event.cpp b/pmu/dummy_event.cpp index 723a753..9137db8 100644 --- a/pmu/dummy_event.cpp +++ b/pmu/dummy_event.cpp @@ -76,6 +76,7 @@ namespace KUNPENG_PMU { if (forkPidQueue.empty()) { continue; } + std::lock_guard lg(dummyMutex); auto& pid = forkPidQueue.front(); for (const auto& evtList: evtLists) { auto groupId = evtList->GetGroupId(); @@ -83,7 +84,6 @@ namespace KUNPENG_PMU { DummyContext ctx = {evtList, static_cast(pid), evtGroupInfo.first, evtGroupInfo.second}; forkStrategy.DoHandler(ctx, evtGroupInfo.first, evtGroupInfo.second); } - std::lock_guard lg(dummyMutex); forkPidQueue.pop(); } }); @@ -146,7 +146,9 @@ namespace KUNPENG_PMU { if (header->type == PERF_RECORD_FORK) { auto sample = (KUNPENG_PMU::PerfRecordFork*) header; std::lock_guard lg(dummyMutex); - forkPidQueue.push(sample->tid); + if((uint8_t*)page + MAP_LEN > ringBuf + off + sizeof(KUNPENG_PMU::PerfRecordFork)) { + forkPidQueue.push(sample->tid); + } } if (header->type == PERF_RECORD_EXIT) { auto sample = (KUNPENG_PMU::PerfRecordFork*) header; diff --git a/pmu/dummy_event.h b/pmu/dummy_event.h index 0468677..dc25970 100644 --- a/pmu/dummy_event.h +++ b/pmu/dummy_event.h @@ -65,7 +65,7 @@ namespace KUNPENG_PMU { std::thread dummyThread; std::thread consumeThread; - std::atomic dummyFlag; + volatile std::atomic dummyFlag; std::vector>& evtLists; std::vector ppids; diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp index bee7fa8..73ca53a 100644 --- a/pmu/evt_list.cpp +++ b/pmu/evt_list.cpp @@ -109,6 +109,11 @@ int KUNPENG_PMU::EvtList::Init(const bool groupEnable, const std::shared_ptr /proc/sys/kernel/perf_event_paranoid'"); + } + if (err == UNKNOWN_ERROR) { pcerr::SetCustomErr(err, std::string{strerror(errno)}); } @@ -176,6 +181,9 @@ void KUNPENG_PMU::EvtList::FillFields( int KUNPENG_PMU::EvtList::Read(vector& data, std::vector& sampleIps, std::vector& extPool, std::vector& switchData) { + + std::unique_lock lg(mutex); + for (unsigned int row = 0; row < numCpu; row++) { for (unsigned int col = 0; col < numPid; col++) { int err = this->xyCounterArray[row][col]->BeginRead(); @@ -245,13 +253,15 @@ void KUNPENG_PMU::EvtList::AddNewProcess(pid_t pid, const bool groupEnable, cons return; } std::unique_lock lock(mutex); + this->pidList.emplace_back(shared_ptr(topology, FreeProcTopo)); + bool hasInitErr = false; + std::map perfEvtMap; for (unsigned int row = 0; row < numCpu; row++) { - this->pidList.emplace_back(shared_ptr(topology, FreeProcTopo)); - procMap[pid] = this->pidList.back(); PerfEvtPtr perfEvt = this->MapPmuAttr(this->cpuList[row]->coreId, this->pidList.back()->tid, this->pmuEvt.get()); if (perfEvt == nullptr) { - return; + hasInitErr = true; + break; } perfEvt->SetSymbolMode(symMode); perfEvt->SetBranchSampleFilter(branchSampleFilter); @@ -263,22 +273,36 @@ void KUNPENG_PMU::EvtList::AddNewProcess(pid_t pid, const bool groupEnable, cons err = perfEvt->Init(groupEnable, -1, -1); } if (err != SUCCESS) { - return; + hasInitErr = true; + break; } - fdList.insert(perfEvt->GetFd()); + perfEvtMap.emplace(row, perfEvt); + } + + if (!hasInitErr) { + procMap[pid] = this->pidList.back(); numPid++; - this->xyCounterArray[row].emplace_back(perfEvt); - /** - * If the current status is enable, start, read, other existing perfEvt may have been enabled and is counting, - * so the new perfEvt must also be added to enable. If the current status is read, the status of all perfEvt - * may be disable. At this time No need to collect counts. - */ - if (evtStat == ENABLE || evtStat == START) { - perfEvt->Enable(); + for (unsigned int row = 0; row < numCpu; row++) { + auto perfEvt = perfEvtMap[row]; + fdList.insert(perfEvt->GetFd()); + this->xyCounterArray[row].emplace_back(perfEvt); + /** + * If the current status is enable, start, read, other existing perfEvt may have been enabled and is counting, + * so the new perfEvt must also be added to enable. If the current status is read, the status of all perfEvt + * may be disable. At this time No need to collect counts. + */ + if (evtStat == ENABLE || evtStat == START) { + perfEvt->Enable(); + } + if (evtStat == READ && prevStat != DISABLE) { + perfEvt->Enable(); + } } - if (evtStat == READ && prevStat != DISABLE) { - perfEvt->Enable(); + } else { + for (const auto& evtPtr : perfEvtMap) { + close(evtPtr.second->GetFd()); } + this->pidList.erase(this->pidList.end() - 1); } } @@ -302,7 +326,7 @@ void KUNPENG_PMU::EvtList::ClearExitFd() int pid = it->get()->GetPid(); if (exitPidVet.find(pid) != exitPidVet.end()) { int fd = it->get()->GetFd(); - this->fdList.erase(fd); + this->fdList.erase(this->fdList.find(fd)); close(fd); it = perfVet.erase(it); continue; diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index cba1713..4ffd5d7 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -427,6 +427,7 @@ static void PmuTaskAttrFree(PmuTaskAttr *taskAttr) int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr) { SetWarn(SUCCESS); + New(SUCCESS); PmuAttr copiedAttr = *attr; pair previousEventList = {0, nullptr}; try { @@ -830,24 +831,6 @@ static void PrepareCpuList(PmuAttr *attr, PmuTaskAttr *taskParam, PmuEvt* pmuEvt } } -static bool PerfEventSupported(__u64 type, __u64 config) -{ - perf_event_attr attr{}; - memset(&attr, 0, sizeof(attr)); - attr.size = sizeof(struct perf_event_attr); - attr.type = type; - attr.config = config; - attr.disabled = 1; - attr.inherit = 1; - attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; - int fd = KUNPENG_PMU::PerfEventOpen(&attr, -1, 0, -1, 0); - if (fd < 0) { - return false; - } - close(fd); - return true; -} - static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *attr, const char* evtName, const int group_id) { unique_ptr taskParam(CreateNode(), PmuTaskAttrFree); @@ -879,18 +862,6 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att #endif return nullptr; } - - if (!PerfEventSupported(pmuEvt->type, pmuEvt->config)) { - int err = MapErrno(errno); - if (err == LIBPERF_ERR_NO_PERMISSION) { - New(LIBPERF_ERR_NO_PERMISSION, "Current user does not have the permission to collect the event.Swtich to the root user and run the 'echo -1 > /proc/sys/kernel/perf_event_paranoid'"); - } else if(err == UNKNOWN_ERROR) { - New(UNKNOWN_ERROR, std::string{strerror(errno)}); - } else { - New(err); - } - return nullptr; - } } /** * Assign cpus to collect diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index d0a47c4..d82e33e 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -444,6 +444,7 @@ namespace KUNPENG_PMU { void PmuList::Close(const int pd) { + EraseDummyEvent(pd); auto evtList = GetEvtList(pd); for (auto item: evtList) { item->Close(); @@ -455,7 +456,6 @@ namespace KUNPENG_PMU { EraseDataEvtGroupList(pd); RemoveEpollFd(pd); EraseSpeCpu(pd); - EraseDummyEvent(pd); EraseParentEventMap(pd); SymResolverDestroy(); PmuEventListFree(); -- Gitee From b58532ba88b6fc9a9a1e99970241364db414f185 Mon Sep 17 00:00:00 2001 From: echodo <2220386943@qq.com> Date: Wed, 11 Jun 2025 14:50:33 +0800 Subject: [PATCH 14/15] =?UTF-8?q?=E5=AE=8C=E5=96=84=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.en.md | 158 +++++++++++++++----------- README.md | 150 ++++++++++++++----------- docs/Details_Usage.md | 252 +++++++++++++++++++++++++++++++++++++++--- docs/Go_API.md | 16 +-- docs/Python_API.md | 16 ++- 5 files changed, 434 insertions(+), 158 deletions(-) diff --git a/README.en.md b/README.en.md index 234f2cc..8ea06f3 100644 --- a/README.en.md +++ b/README.en.md @@ -127,82 +127,106 @@ Here are some examples: * Get pmu count for a process ```C++ -int pidList[1]; -pidList[0] = pid; -char *evtList[1]; -evtList[0] = "cycles"; -// Initialize event list and pid list in PmuAttr. -// There is one event in list, named 'cycles'. -PmuAttr attr = {0}; -attr.evtList = evtList; -attr.numEvt = 1; -attr.pidList = pidList; -attr.numPid = 1; -// Call PmuOpen and pmu descriptor is return. -// is an identity for current task. -int pd = PmuOpen(COUNTING, &attr); -// Start collection. -PmuEnable(pd); -// Collect for one second. -sleep(1); -// Stop collection. -PmuDisable(pd); -PmuData *data = NULL; -// Read pmu data. You can also read data before PmuDisable. -int len = PmuRead(pd, &data); -for (int i = 0; i < len; ++i) { - ... +#include + +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +int main() { + int pid = getpid(); + int pidList[1]; + pidList[0] = pid; + char *evtList[1]; + evtList[0] = "cycles"; + // Initialize event list and pid list in PmuAttr. + // There is one event in list, named 'cycles'. + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = 1; + attr.pidList = pidList; + attr.numPid = 1; + // Call PmuOpen and pmu descriptor is return. + // is an identity for current task. + int pd = PmuOpen(COUNTING, &attr); + // Start collection. + PmuEnable(pd); + // Collect for one second. + sleep(1); + // Stop collection. + PmuDisable(pd); + PmuData *data = NULL; + // Read pmu data. You can also read data before PmuDisable. + int len = PmuRead(pd, &data); + for (int i = 0; i < len; ++i) { + PmuData *d = &data[i]; + std::cout << "evt=" << d->evt << "count=" << d->count << std::endl; + } + // To free PmuData, call PmuDataFree. + PmuDataFree(data); + // Like fd, call PmuClose if pd will not be used. + PmuClose(pd); } -// To free PmuData, call PmuDataFree. -PmuDataFree(data); -// Like fd, call PmuClose if pd will not be used. -PmuClose(pd); + ``` * Sample a process ```C++ -int pidList[1]; -pidList[0] = pid; -char *evtList[1]; -evtList[0] = "cycles"; -// Initialize event list and pid list in PmuAttr. -// There is one event in list, named 'cycles'. -PmuAttr attr = {0}; -attr.evtList = evtList; -attr.numEvt = 1; -attr.pidList = pidList; -attr.numPid = 1; -// Call PmuOpen and pmu descriptor is return. -// is an identity for current task. -// Use SAMPLING for sample task. -int pd = PmuOpen(SAMPLING, &attr); -// Start collection. -PmuEnable(pd); -// Collect for one second. -sleep(1); -// Stop collection. -PmuDisable(pd); -PmuData *data = NULL; -// Read pmu data. You can also read data before PmuDisable. -int len = PmuRead(pd, &data); -for (int i = 0; i < len; ++i) { - // Get an element from array. - PmuData *d = &data[i]; - // Get stack object which is a linked list. - Stack *stack = d->stack; - while (stack) { - // Get symbol object. - if (stack->symbol) { - ... +#include + +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +int main() { + int pid = getpid(); + int pidList[1]; + pidList[0] = pid; + char *evtList[1]; + evtList[0] = "cycles"; + // Initialize event list and pid list in PmuAttr. + // There is one event in list, named 'cycles'. + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = 1; + attr.pidList = pidList; + attr.numPid = 1; + // Call PmuOpen and pmu descriptor is return. + // is an identity for current task. + // Use SAMPLING for sample task. + int pd = PmuOpen(SAMPLING, &attr); + // Start collection. + PmuEnable(pd); + // Collect for one second. + sleep(1); + // Stop collection. + PmuDisable(pd); + PmuData *data = NULL; + // Read pmu data. You can also read data before PmuDisable. + int len = PmuRead(pd, &data); + for (int i = 0; i < len; ++i) { + // Get an element from array. + PmuData *d = &data[i]; + // Get stack object which is a linked list. + Stack *stack = d->stack; + while (stack) { + // Get symbol object. + if (stack->symbol) { + Symbol *data = stack->symbol; + std::cout << std::hex << data->addr << " " << data->symbolName << "+0x" << data->offset << " " + << data->codeMapAddr << " (" << data->module << ")" + << " (" << std::dec << data->fileName << ":" << data->lineNum << ")" << std::endl; + } + stack = stack->next; } - stack = stack->next; } + // To free PmuData, call PmuDataFree. + PmuDataFree(data); + // Like fd, call PmuClose if pd will not be used. + PmuClose(pd); } -// To free PmuData, call PmuDataFree. -PmuDataFree(data); -// Like fd, call PmuClose if pd will not be used. -PmuClose(pd); + ``` * Python examples diff --git a/README.md b/README.md index 555c062..5b4bfea 100644 --- a/README.md +++ b/README.md @@ -107,77 +107,101 @@ Go API文档可以参考GO_API.md: - 获取进程的pmu计数 ```C++ -int pidList[1]; -pidList[0] = pid; -char *evtList[1]; -evtList[0] = "cycles"; -// 初始化事件列表,指定需要计数的事件cycles。 -PmuAttr attr = {0}; -attr.evtList = evtList; -attr.numEvt = 1; -attr.pidList = pidList; -attr.numPid = 1; -// 调用PmuOpen,返回pd。pd表示该任务的id。 -int pd = PmuOpen(COUNTING, &attr); -// 开始采集。 -PmuEnable(pd); -// 采集1秒。 -sleep(1); -// 停止采集。 -PmuDisable(pd); -PmuData *data = NULL; -// 读取PmuData,它是一个数组,长度是len。 -int len = PmuRead(pd, &data); -for (int i = 0; i < len; ++i) { - PmuData *d = &data[i]; - ... +#include +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +int main() { + int pidList[1]; + pidList[0] = getpid(); + char *evtList[1]; + evtList[0] = "cycles"; + // 初始化事件列表,指定需要计数的事件cycles。 + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = 1; + attr.pidList = pidList; + attr.numPid = 1; + // 调用PmuOpen,返回pd。pd表示该任务的id。 + int pd = PmuOpen(COUNTING, &attr); + // 开始采集。 + PmuEnable(pd); + // 采集1秒。 + sleep(1); + // 停止采集。 + PmuDisable(pd); + PmuData *data = NULL; + // 读取PmuData,它是一个数组,长度是len。 + int len = PmuRead(pd, &data); + for (int i = 0; i < len; ++i) { + PmuData *d = &data[i]; + std::cout << "evt=" << d->evt << "count=" << d->count << std::endl; + } + // 释放PmuData。 + PmuDataFree(data); + // 类似fd,当任务结束时调用PmuClose释放资源。 + PmuClose(pd); } -// 释放PmuData。 -PmuDataFree(data); -// 类似fd,当任务结束时调用PmuClose释放资源。 -PmuClose(pd); ``` - 对进程进行采样 ```C++ -int pidList[1]; -pidList[0] = pid; -char *evtList[1]; -evtList[0] = "cycles"; -// 初始化事件列表,指定需要计数的事件cycles。 -PmuAttr attr = {0}; -attr.evtList = evtList; -attr.numEvt = 1; -attr.pidList = pidList; -attr.numPid = 1; -// 调用PmuOpen,返回pd。pd表示该任务的id。 -int pd = PmuOpen(SAMPLING, &attr); -// 开始采集。 -PmuEnable(pd); -// 采集1秒。 -sleep(1); -// 停止采集。 -PmuDisable(pd); -PmuData *data = NULL; -// 读取PmuData,它是一个数组,长度是len。 -int len = PmuRead(pd, &data); -for (int i = 0; i < len; ++i) { - // 获取数组的一个元素。 - PmuData *d = &data[i]; - // 获取调用栈对象,它是一个链表。 - Stack *stack = d->stack; - while (stack) { - // 获取符号对象。 - if (stack->symbol) { - ... +#include + +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +int main() { + int pid = getpid(); + int pidList[1]; + pidList[0] = pid; + char *evtList[1]; + evtList[0] = "cycles"; + // 初始化事件列表,指定需要计数的事件cycles。 + PmuAttr attr = {0}; + attr.evtList = evtList; + attr.numEvt = 1; + attr.pidList = pidList; + attr.numPid = 1; + attr.symbolMode = RESOLVE_ELF_DWARF; + attr.callStack = 1; + attr.freq = 200; + attr.useFreq = 1; + // 调用PmuOpen,返回pd。pd表示该任务的id。 + int pd = PmuOpen(SAMPLING, &attr); + // 开始采集。 + PmuEnable(pd); + // 采集1秒。 + sleep(1); + // 停止采集。 + PmuDisable(pd); + PmuData *data = NULL; + // 读取PmuData,它是一个数组,长度是len。 + int len = PmuRead(pd, &data); + for (int i = 0; i < len; ++i) { + // 获取数组的一个元素。 + PmuData *d = &data[i]; + // 获取调用栈对象,它是一个链表。 + Stack *stack = d->stack; + while (stack) { + // 获取符号对象。 + if (stack->symbol) { + Symbol *data = stack->symbol; + std::cout << std::hex << data->addr << " " << data->symbolName << "+0x" << data->offset << " " + << data->codeMapAddr << " (" << data->module << ")" + << " (" << std::dec << data->fileName << ":" << data->lineNum << ")" << std::endl; + + } + stack = stack->next; } - stack = stack->next; } + // 释放PmuData。 + PmuDataFree(data); + // 类似fd,当任务结束时调用PmuClose释放资源。 + PmuClose(pd); } -// 释放PmuData。 -PmuDataFree(data); -// 类似fd,当任务结束时调用PmuClose释放资源。 -PmuClose(pd); ``` - Python 例子 diff --git a/docs/Details_Usage.md b/docs/Details_Usage.md index fe89cc6..4ee4eee 100644 --- a/docs/Details_Usage.md +++ b/docs/Details_Usage.md @@ -25,6 +25,7 @@ int pd = PmuOpen(COUNTING, &attr); # python代码示例 import time import kperf + evtList = ["cycles", "branch-misses"] pmu_attr = kperf.PmuAttr(evtList=evtList) pd = kperf.open(kperf.PmuTaskType.COUNTING, pmu_attr) @@ -134,15 +135,38 @@ perf record -e cycles,branch-misses 设置PmuAttr的方式和Counting一样,在调用PmuOpen的时候,把任务类型设置为SAMPLING,并且设置采样频率: ```c++ // c++代码示例 +#include +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +PmuAttr attr = {0}; +char* evtList[1] = {"cycles"}; attr.freq = 1000; // 采样频率是1000HZ attr.useFreq = 1; +attr.evtList = evtList; +attr.numEvt = 1; int pd = PmuOpen(SAMPLING, &attr); +if ( pd == -1) { + printf("kperf pmuopen counting failed, expect err is nil, but is %s\n", Perror()); +} +PmuEnable(pd); +sleep(1); +PmuDisable(pd); +PmuData* data = nullptr; +int len = PmuRead(pd, &data); +for (int i = 0; i < len; i++) { + printf("cpu=%d pid=%d tid=%d period=%ld\n", data[i].cpu, data[i].pid, data[i].tid, data[i].period); +} +PmuClose(pd); ``` ```python # python代码示例 import kperf +import ksym import time + evtList = ["branch-misses", "cycles"] pmu_attr = kperf.PmuAttr( evtList=evtList, @@ -150,6 +174,16 @@ pmu_attr = kperf.PmuAttr( symbolMode=kperf.SymbolMode.RESOLVE_ELF ) pd = kperf.open(kperf.PmuTaskType.SAMPLING, pmu_attr) +if pd == -1: + print(f"kperf pmuopen sample failed, expect err is nil, but is {kperf.error()}\n") +kperf.enable(pd) +time.sleep(1) +kperf.disable(pd) + +pmu_data = kperf.read(pd) +for item in pmu_data.iter: + print(f"cpu {item.cpu} pid {item.pid} tid {item.tid} period {item.period}") +kperf.close(pd) ``` ```go @@ -165,6 +199,18 @@ func main() { fmt.Printf("kperf pmuopen sample failed, expect err is nil, but is %v\n", err) return } + kperf.PmuEnable(pd) + time.Sleep(time.Second) + kperf.PmuDisable(pd) + dataVo, err := kperf.PmuRead(pd) + if err != nil { + fmt.Printf("kperf pmuread failed, expect err is nil, but is %v\n", err) + return + } + for _, o := range dataVo.GoData { + fmt.Printf("cpu=%d pid=%d tid=%d period=%v\n", o.Cpu, o.Pid, o.Tid, o.Period) + } + kperf.PmuClose(pd) } ``` @@ -197,23 +243,55 @@ perf record -e arm_spe_0/load_filter=1/ 对于libkperf,可以这样设置PmuAttr: ```c++ // c++代码示例 +#include + +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + PmuAttr attr = {0}; attr.period = 8192; // 采样周期是8192 attr.dataFilter = LOAD_FILTER; // 设置filter属性为load_filter + +int pd = PmuOpen(SPE_SAMPLING, &attr); +if ( pd == -1) { + printf("kperf pmuopen counting failed, expect err is nil, but is %s\n", Perror()); +} +PmuEnable(pd); +sleep(1); +PmuDisable(pd); +PmuData* data = nullptr; +int len = PmuRead(pd, &data); +for (int i = 0; i < len; i++) { + auto o = data[i]; + printf("spe base info comm=%s, pid=%d, tid=%d, coreId=%d, numaId=%d, sockedId=%d\n", o.comm, o.pid, o.tid, o.cpuTopo->coreId, o.cpuTopo->numaId, o.cpuTopo->socketId); + printf("spe ext info pa=%lu, va=%lu, event=%lu, latency=%lu\n", o.ext->pa, o.ext->va, o.ext->event, o.ext->lat); +} +PmuClose(pd); ``` ```python # python代码示例 import kperf +import ksym +import time + pmu_attr = kperf.PmuAttr( - sampleRate = 1000, - symbolMode = kperf.SymbolMode.RESOLVE_ELF, - dataFilter = kperf.SpeFilter.SPE_DATA_ALL, - evFilter = kperf.SpeEventFilter.SPE_EVENT_RETIRED, - minLatency = 0x40 + sampleRate = 8192, + dataFilter = kperf.SpeFilter.LOAD_FILTER, ) # 需要root权限才能运行 pd = kperf.open(kperf.PmuTaskType.SPE_SAMPLING, pmu_attr) + +kperf.enable(pd) +time.sleep(1) +kperf.disable(pd) + +pmu_data = kperf.read(pd) +for item in pmu_data.iter: + print(f"spe base info comm={item.comm}, pid={item.pid}, tid={item.tid}, coreId={item.cpuTopo.coreId}, numaId={item.cpuTopo.numaId}, sockedId={item.cpuTopo.socketId}") + print(f"spe ext info pa={item.ext.pa}, va={item.ext.va}, event={item.ext.event}, latency={item.ext.lat}\n") +kperf.close(pd) ``` ```go @@ -222,12 +300,28 @@ import "libkperf/kperf" import "time" func main() { - attr := kperf.PmuAttr{MinLatency:0x40, SymbolMode: kperf.ELF, SampleRate: 1000, DataFilter: kperf.SPE_DATA_ALL, EvFilter: kperf.SPE_EVENT_RETIRED} + attr := kperf.PmuAttr{SampleRate:8192, DataFilter: kperf.LOAD_FILTER} pd, err := kperf.PmuOpen(kperf.SPE, attr) if err != nil { fmt.Printf("kperf pmuopen spe failed, expect err is nil, but is %v\n", err) return } + + kperf.PmuEnable(pd) + time.Sleep(time.Second) + kperf.PmuDisable(pd) + + dataVo, err := kperf.PmuRead(pd) + if err != nil { + fmt.Printf("kperf pmuread failed, expect err is nil, but is %v\n", err) + } + + for _, o := range dataVo.GoData { + fmt.Printf("spe base info comm=%v, pid=%v, tid=%v, coreId=%v, numaId=%v, sockedId=%v\n", o.Comm, o.Pid, o.Tid, o.CpuTopo.CoreId, o.CpuTopo.NumaId, o.CpuTopo.SocketId) + fmt.Printf("spe ext info pa=%v, va=%v, event=%v, latency=%v\n", o.SpeExt.Pa, o.SpeExt.Va, o.SpeExt.Event, o.SpeExt.Lat) + } + kperf.PmuDataFree(dataVo) + kperf.PmuClose(pd) } ``` @@ -311,13 +405,35 @@ PmuAttr attr = {0}; attr.evtList = evtList; attr.numEvt = 1; int pd = PmuOpen(COUNTING, &attr); +if ( pd == -1) { + printf("kperf pmuopen counting failed, expect err is nil, but is %s\n", Perror()); +} +PmuEnable(pd); +sleep(1); +PmuDisable(pd); +PmuData* data = nullptr; +int len = PmuRead(pd, &data); +for (int i = 0; i < len; i++) { + printf("evt=%s, count=%d\n", data[i].evt, data[i].count); +} +PmuClose(pd); ``` + ```python # python代码示例 import kperf +import time + evtList = ["hisi_sccl1_ddrc0/flux_rd/"] pmu_attr = kperf.PmuAttr(evtList=evtList) pd = kperf.open(kperf.PmuTaskType.COUNTING, pmu_attr) +kperf.enable(pd) +time.sleep(1) +kperf.disable(pd) +pmu_data = kperf.read(pd) +for item in pmu_data.iter: + print(f"evt={item.evt} count={item.count}") +kperf.close(pd) ``` ```go @@ -334,6 +450,18 @@ func main() { fmt.Printf("kperf pmuopen counting failed, expect err is nil, but is %v\n", err) return } + kperf.PmuEnable(pd) + time.Sleep(time.Second) + kperf.PmuDisable(pd) + dataVo, err := kperf.PmuRead(pd) + if err != nil { + fmt.Printf("kperf pmuread failed, expect err is nil, but is %v\n", err) + return + } + for _, o := range dataVo.GoData { + fmt.Printf("evt=%v count=%v \n", o.Evt, o.Count) + } + kperf.PmuClose(pd) } ``` @@ -350,7 +478,7 @@ evtList[0] = "hisi_sccl1_ddrc/flux_rd/"; evtList = ["hisi_sccl1_ddrc/flux_rd/"] ``` -```go +```goa // go代码示例 evtList := []string{"hisi_sccl1_ddrc/flux_rd/"} ``` @@ -380,16 +508,27 @@ libkperf支持tracepoint的采集,支持的tracepoint事件可以通过perf li 可以这样设置PmuAttr: ```c++ // c++代码示例 +#include +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + char *evtList[1]; evtList[0] = "sched:sched_switch"; PmuAttr attr = {0}; attr.evtList = evtList; attr.numEvt = 1; +attr.period = 1000; int pd = PmuOpen(SAMPLING, &attr); ``` ```python # python代码示例 +import kperf +import ksym +import time +from ctypes import * + evtList = ["sched:sched_switch"] pmu_attr = kperf.PmuAttr( evtList=evtList, @@ -404,7 +543,6 @@ pd = kperf.open(kperf.PmuTaskType.SAMPLING, pmu_attr) import "libkperf/kperf" import "fmt" - func main() { evtList := []string{"sched:sched_switch"} attr := kperf.PmuAttr{EvtList:evtList, SymbolMode:kperf.ELF, SampleRate: 1000} @@ -424,10 +562,24 @@ tracepoint能够获取每个事件特有的数据,比如sched:sched_switch包 libkperf提供了接口PmuGetField来获取tracepoint的数据。比如对于sched:sched_switch,可以这样调用: ```c++ // c++代码示例 -int prev_pid; -PmuGetField(pmuData->rawData, "prev_pid", &prev_pid, sizeof(prev_pid)); -char next_comm[16]; -PmuGetField(pmuData->rawData, "next_comm", &next_comm, sizeof(next_comm)); +#include +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + +PmuEnable(pd); +sleep(1); +PmuDisable(pd); +PmuData* data = nullptr; +int len = PmuRead(pd, &data); +for (int i = 0; i < len; i++) { + auto pmuData = &data[i]; + int prev_pid; + PmuGetField(pmuData->rawData, "prev_pid", &prev_pid, sizeof(prev_pid)); + char next_comm[16]; + PmuGetField(pmuData->rawData, "next_comm", &next_comm, sizeof(next_comm)); + printf("next_comm=%s;prev_pid=%d\n", next_comm, prev_pid); +} ``` ```python @@ -480,10 +632,10 @@ func main() { var cArray [15]C.char nextErr := v.GetField("next_comm", unsafe.Pointer(&cArray)) if nextErr != nil { - fmt.Printf("get next_comm failed err is%v ",nextErr) + fmt.Printf("get next_comm failed err is%v\n",nextErr) } else { ptr := (*C.char)(unsafe.Pointer(&cArray[0])) - fmt.Printf("next_comm=%v;", C.GoString(ptr)) + fmt.Printf("next_comm=%v\n", C.GoString(ptr)) } prevPid := C.int(0) @@ -509,6 +661,11 @@ perf stat -e "{cycles,branch-loads,branch-load-misses,iTLB-loads}",inst_retired 比如,可以这样调用: ```c++ // c++代码示例 +#include +#include "symbol.h" +#include "pmu.h" +#include "pcerrc.h" + unsigned numEvt = 5; char *evtList[numEvt] = {"cycles","branch-loads","branch-load-misses","iTLB-loads","inst_retired"}; // 前四个事件是一个分组 @@ -517,12 +674,27 @@ PmuAttr attr = {0}; attr.evtList = evtList; attr.numEvt = numEvt; attr.evtAttr = groupId; + +int pd = PmuOpen(COUNTING, &attr); +if ( pd == -1) { + printf("kperf pmuopen counting failed, expect err is nil, but is %s\n", Perror()); +} +PmuEnable(pd); +sleep(1); +PmuDisable(pd); +PmuData* data = nullptr; +int len = PmuRead(pd, &data); +for (int i = 0; i < len; i++) { + printf("evt=%s, count=%d evt=%d\n", data[i].evt, data[i].count, data[i].evt); +} +PmuClose(pd); ``` ```python # python代码示例 import kperf import time + evtList = ["cycles","branch-loads","branch-load-misses","iTLB-loads","inst_retired"] # 前四个事件是一个分组 evtAttrList = [1,1,1,1,-1] @@ -535,6 +707,7 @@ pmu_data = kperf.read(pd) pd = kperf.open(kperf.PmuTaskType.SAMPLING, pmu_attr) for data in pmu_data.iter: print(f"cpu {data.cpu} count {data.count} evt {data.evt}") +kperf.close(pd) ``` ```go @@ -605,6 +778,10 @@ pmu_attr = kperf.PmuAttr(evtList=evtList, includeNewFork=True) 参考代码: ```c++ // c++代码示例 +#include +#include "symbol.h" +#include "pmu.h" + PmuDeviceAttr devAttr[2]; // DDR读带宽 devAttr[0].metric = PMU_DDR_READ_BW; @@ -627,10 +804,10 @@ for (int i = 0; i < len / 2; ++i) { // channelID表示数据对应的通道ID。 // count是距离上次采集的DDR总读/写包长,单位是Byte, // 需要除以时间间隔得到带宽(这里的时间间隔是1秒)。 - cout << "read bandwidth(Socket: " << devData[i].socketId << " Numa: " << devData[i].ddrNumaId << " Channel: " << devData[i].channelId << "): " << devData[i].count/1024/1024 << "M/s\n"; + std::cout << "read bandwidth(Socket: " << devData[i].socketId << " Numa: " << devData[i].ddrNumaId << " Channel: " << devData[i].channelId << "): " << devData[i].count/1024/1024 << "M/s\n"; } for (int i = len / 2; i < len; ++i) { - cout << "write bandwidth(Socket: " << devData[i].socketId << " Numa: " << devData[i].ddrNumaId << " Channel: " << devData[i].channelId << "): " << devData[i].count/1024/1024 << "M/s\n"; + std::cout << "write bandwidth(Socket: " << devData[i].socketId << " Numa: " << devData[i].ddrNumaId << " Channel: " << devData[i].channelId << "): " << devData[i].count/1024/1024 << "M/s\n"; } DevDataFree(devData); PmuDataFree(oriData); @@ -639,6 +816,9 @@ PmuDisable(pd); ```python # python代码示例 +import kperf +import time + dev_attr = [ kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_DDR_READ_BW), kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_DDR_WRITE_BW) @@ -658,6 +838,10 @@ for data in dev_data.iter: ```go // go代码用例 +import "libkperf/kperf" +import "fmt" +import "time" + deviceAttrs := []kperf.PmuDeviceAttr{kperf.PmuDeviceAttr{Metric: kperf.PMU_DDR_READ_BW}, kperf.PmuDeviceAttr{Metric: kperf.PMU_DDR_WRITE_BW}} fd, _ := kperf.PmuDeviceOpen(deviceAttrs) kperf.PmuEnable(fd) @@ -705,6 +889,10 @@ libkperf提供了采集L3 cache平均时延的能力,用于分析访存型应 参考代码: ```c++ +#include +#include "symbol.h" +#include "pmu.h" + // c++代码示例 PmuDeviceAttr devAttr[1]; // L3平均时延 @@ -721,7 +909,7 @@ auto len = PmuGetDevMetric(oriData, oriLen, devAttr, 1, &devData); // devData的长度等于cluster个数 for (int i=0;i +#include "symbol.h" +#include "pmu.h" + PmuDeviceAttr devAttr[1]; // 采集PCIE设备RX的读带宽 devAttr[0].metric = PMU_PCIE_RX_MRD_BW; @@ -802,6 +1001,9 @@ PmuDisable(pd); ```python # python代码示例 +import kperf +import time + dev_attr = [ kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_PCIE_RX_MRD_BW, bdf="16:04.0") ] @@ -817,6 +1019,10 @@ for data in dev_data.iter: ```go // go代码用例 +import "libkperf/kperf" +import "fmt" +import "time" + deviceAttrs := []kperf.PmuDeviceAttr{kperf.PmuDeviceAttr{Metric: kperf.PMU_PCIE_RX_MRD_BW, Bdf: "16:04.0"}} fd, _ := kperf.PmuDeviceOpen(deviceAttrs) kperf.PmuEnable(fd) @@ -848,6 +1054,10 @@ perf trace -e read,write 比如,可以这样调用: ```c++ // c++代码示例 +#include +#include "symbol.h" +#include "pmu.h" + unsigned numFunc = 2; const char *funs1 = "read"; const char *funs2 = "write"; @@ -862,7 +1072,7 @@ PmuTraceDisable(pd); PmuTraceData *data = nullptr; int len = PmuTraceRead(pd, &data); for(int i = 0; i < len; ++i) { - printf("funcName: %s, elspsedTime: %f ms pid: %d tid: %d cpu: %d comm: %s", data[i].funcs, data[i].elapsedTime, data[i].pid, data[i].tid, data[i].cpu, data[i].comm) + printf("funcName: %s, elapsedTime: %f ms pid: %d tid: %d cpu: %d comm: %s", data[i].funcs, data[i].elapsedTime, data[i].pid, data[i].tid, data[i].cpu, data[i].comm); } PmuTraceClose(pd); ``` @@ -871,6 +1081,7 @@ PmuTraceClose(pd); # python代码示例 import kperf import time + funcList = ["read","write"] pmu_trace_attr = kperf.PmuTraceAttr(funcs=funcList) pd = kperf.trace_open(kperf.PmuTraceType.TRACE_SYS_CALL, pmu_trace_attr) @@ -930,6 +1141,10 @@ funcName: write elapsedTime: 0.00118 ms pid: 997235 tid: 997235 cpu: 110 comm: t ### 采集BRBE数据 libkperf基于sampling的能力,增加了对branch sample stack数据的采集能力,用于获取CPU的跳转记录, 通过branchSampleFilter可指定获取不同类型的分支跳转记录。 ```c++ +#include +#include "symbol.h" +#include "pmu.h" + char* evtList[1] = {"cycles"}; int* cpuList = nullptr; PmuAttr attr = {0}; @@ -980,6 +1195,7 @@ ffff88f60aa0->ffff88f60618 1 ```python import time +import ksym import kperf evtList = ["cycles"] diff --git a/docs/Go_API.md b/docs/Go_API.md index fd0a346..30a5c3f 100644 --- a/docs/Go_API.md +++ b/docs/Go_API.md @@ -300,7 +300,7 @@ import "fmt" func main() { syscallList := kperf.PmuSysCallFuncList() if syscallList == nil { - fmt.Printf("sys call list is empty") + fmt.Printf("sys call list is empty\n") } else { for _, funcName := range syscallList { fmt.Printf("func name %v\n", funcName) @@ -478,12 +478,12 @@ import "fmt" func main() { err := kperf.PmuOpenCpuFreqSampling(100) if err != nil { - fmt.Printf("kperf PmuOpenCpuFreqSampling failed, expect err is nil, but is %v", err) + fmt.Printf("kperf PmuOpenCpuFreqSampling failed, expect err is nil, but is %v\n", err) } freqList := kperf.PmuReadCpuFreqDetail() for _, v := range freqList { - fmt.Printf("cpuId=%v, minFreq=%d, maxFreq=%d, avgFreq=%d", v.CpuId, v.MinFreq, v.MaxFreq, v.AvgFreq) + fmt.Printf("cpuId=%v, minFreq=%d, maxFreq=%d, avgFreq=%d\n", v.CpuId, v.MinFreq, v.MaxFreq, v.AvgFreq) } kperf.PmuCloseCpuFreqSampling() @@ -501,7 +501,7 @@ func main() { attr := kperf.PmuAttr{EvtList:[]string{"cycles"}, CallStack:true, SampleRate: 1000, UseFreq:true} fd, err := kperf.PmuOpen(kperf.SAMPLE, attr) if err != nil { - fmt.Printf("kperf pmuopen sample failed, expect err is nil, but is %v", err) + fmt.Printf("kperf pmuopen sample failed, expect err is nil, but is %v\n", err) return } @@ -511,24 +511,24 @@ func main() { dataVo, err := kperf.PmuRead(fd) if err != nil { - fmt.Printf("kperf pmuread failed, expect err is nil, but is %v", err) + fmt.Printf("kperf pmuread failed, expect err is nil, but is %v\n", err) return } for _, o := range dataVo.GoData { if len(o.Symbols) != 0 { - fmt.Printf("expect symbol data is empty, but is not") + fmt.Printf("expect symbol data is empty, but is not\n") } } parseErr := kperf.ResolvePmuDataSymbol(dataVo) if parseErr != nil { - fmt.Printf("kperf ResolvePmuDataSymbol failed, expect err is nil, but is %v", parseErr) + fmt.Printf("kperf ResolvePmuDataSymbol failed, expect err is nil, but is %v\n", parseErr) } for _, o := range dataVo.GoData { if len(o.Symbols) == 0 { - fmt.Printf("expect symbol data is not empty, but is empty") + fmt.Printf("expect symbol data is not empty, but is empty\n") } } kperf.PmuDataFree(dataVo) diff --git a/docs/Python_API.md b/docs/Python_API.md index a0a1968..2ec007a 100644 --- a/docs/Python_API.md +++ b/docs/Python_API.md @@ -82,8 +82,10 @@ kperf.open(collector_type: kperf.PmuTaskType, pmu_attr: kperf.PmuAttr) ```python # python代码示例 -import time import kperf +import ksym +import time + evtList = ["cycles", "branch-misses"] pmu_attr = kperf.PmuAttr(evtList=evtList) pd = kperf.open(kperf.PmuTaskType.COUNTING, pmu_attr) @@ -196,6 +198,7 @@ get_field(pmu_data: ImplPmuData, field_name: str, value: c_void_p) ```python import kperf +import ksym import time from ctypes import * @@ -272,8 +275,9 @@ kperf.trace_open(trace_type: kperf.PmuTraceType, pmu_trace_attr: kperf.PmuTraceA ```python # python代码示例 -import time import kperf +import time + funcs = ["read", "write"] pmu_trace_attr = kperf.PmuTraceAttr(funcs=funcs) pd = kperf.trace_open(kperf.PmuTraceType.TRACE_SYS_CALL, pmu_trace_attr) @@ -346,6 +350,8 @@ kperf.device_open(dev_attr: List[PmuDeviceAttr]) 初始化采集uncore事件指 ```python # python代码示例 +import kperf +import time dev_attr = [ kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_L3_TRAFFIC) ] @@ -443,6 +449,9 @@ def close_cpu_freq_sampling() 关闭cpu频率采集 def read_cpu_freq_detail() -> CpuFreqDetail 读取开启频率采集到读取时间内的cpu最大频率、最小频率以及平均频率 ```python #python代码示例 +import kperf +import time + err = kperf.open_cpu_freq_sampling(100) if err != 0: print(f"error number: {kperf.errorno()} error message: {kperf.error()}") @@ -459,6 +468,9 @@ kperf.close_cpu_freq_sampling() def resolvePmuDataSymbol(pmuData: PmuData) -> int: 当SymbolMode不设置或者设置为0时,可通过该接口解析read返回的PmuData数据中的符号 ```python #python代码示例 +import kperf +import time + event_name = "cycles" pmu_attr = kperf.PmuAttr( evtList=[event_name], -- Gitee From af7130f7a3edc278ebf60c0731d4d77ce9d825b7 Mon Sep 17 00:00:00 2001 From: wuying39 <921169248@qq.com> Date: Thu, 12 Jun 2025 16:00:07 +0800 Subject: [PATCH 15/15] Add HHA uncore event --- docs/Details_Usage.md | 96 +++++++++ docs/Go_API.md | 2 + docs/Python_API.md | 4 +- example/llc_miss_ratio.cpp | 346 --------------------------------- go/src/libkperf/kperf/kperf.go | 18 +- include/pmu.h | 16 +- pmu/pmu_metric.cpp | 121 +++++++++--- python/modules/kperf/pmu.py | 18 +- python/tests/test_metric.py | 22 +++ test/test_perf/test_metric.cpp | 25 +++ 10 files changed, 276 insertions(+), 392 deletions(-) delete mode 100644 example/llc_miss_ratio.cpp diff --git a/docs/Details_Usage.md b/docs/Details_Usage.md index 4ee4eee..6c1d75e 100644 --- a/docs/Details_Usage.md +++ b/docs/Details_Usage.md @@ -1043,6 +1043,102 @@ kperf.PmuClose(fd) pcie bw(16:04.0): 124122412 Bytes/ns ``` +### 采集跨numa/跨socket访问HHA比例 +libkperf提供了采集跨numa/跨socket访问HHA的操作比例的能力,用于分析访存型应用的性能瓶颈,采集以numa为粒度。 + +参考代码: +```c++ +// c++代码示例 +#include +#include "symbol.h" +#include "pmu.h" + +PmuDeviceAttr devAttr[2]; +// 采集跨numa访问HHA的操作比例 +devAttr[0].metric = PMU_HHA_CROSS_NUMA; +// 采集跨socket访问HHA的操作比例 +devAttr[1].metric = PMU_HHA_CROSS_SOCKET; +// 初始化采集任务 +int pd = PmuDeviceOpen(devAttr, 2); +// 开始采集 +PmuEnable(pd); +sleep(1); +PmuData *oriData = nullptr; +int oriLen = PmuRead(pd, &oriData); +PmuDeviceData *devData = nullptr; +auto len = PmuGetDevMetric(oriData, oriLen, devAttr, 2, &devData); +// devData的长度等于设备numa的个数 +for (int i = 0; i < len / 2; ++i) { + cout << "HHA cross-numa operations ratio (Numa: " << devData[i].numaId << "): " << devData[i].count<< "\n"; +} +for (int i = len / 2; i < len; ++i) { + cout << "HHA cross-socket operations ratio (Numa: " << devData[i].numaId << "): " << devData[i].count<< "\n"; +} +DevDataFree(devData); +PmuDataFree(oriData); +PmuDisable(pd); +``` + +```python +# python代码示例 +import kperf +import time + +dev_attr = [ + kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_HHA_CROSS_NUMA), + kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_HHA_CROSS_SOCKET) +] +pd = kperf.device_open(dev_attr) +kperf.enable(pd) +time.sleep(1) +kperf.disable(pd) +ori_data = kperf.read(pd) +dev_data = kperf.get_device_metric(ori_data, dev_attr) +for data in dev_data.iter: + if data.metric == kperf.PmuDeviceMetric.PMU_HHA_CROSS_NUMA: + print(f"HHA cross-numa operations ratio (Numa: {data.numaId}): {data.count}") + if data.metric == kperf.PmuDeviceMetric.PMU_HHA_CROSS_SOCKET: + print(f"HHA cross-socket operations ratio (Numa: {data.numaId}): {data.count}") +``` + +```go +// go代码用例 +import "libkperf/kperf" +import "fmt" +import "time" + +deviceAttrs := []kperf.PmuDeviceAttr{kperf.PmuDeviceAttr{Metric: kperf.PMU_HHA_CROSS_NUMA}, kperf.PmuDeviceAttr{Metric: kperf.PMU_HHA_CROSS_SOCKET}} +fd, _ := kperf.PmuDeviceOpen(deviceAttrs) +kperf.PmuEnable(fd) +time.Sleep(1 * time.Second) +kperf.PmuDisable(fd) +dataVo, _ := kperf.PmuRead(fd) +deivceDataVo, _ := kperf.PmuGetDevMetric(dataVo, deviceAttrs) +for _, v := range deivceDataVo.GoDeviceData { + if v.Metric == kperf.PMU_HHA_CROSS_NUMA { + fmt.Printf("HHA cross-numa operations ratio (Numa: %v): %v\n", v.NumaId, v.Count) + } + if v.Metric == kperf.PMU_HHA_CROSS_SOCKET { + fmt.Printf("HHA cross-socket operations ratio (Numa: %v): %v\n", v.NumaId, v.Count) + } +} +kperf.DevDataFree(deivceDataVo) +kperf.PmuDataFree(dataVo) +kperf.PmuClose(fd) +``` + +执行上述代码,输出的结果类似如下: +``` +HHA cross-numa operations ratio (Numa: 0): 0.438888 +HHA cross-numa operations ratio (Numa: 1): 0.0248052 +HHA cross-numa operations ratio (Numa: 2): 0.0277224 +HHA cross-numa operations ratio (Numa: 3): 0.181404 +HHA cross-socket operations ratio (Numa: 0): 0.999437 +HHA cross-socket operations ratio (Numa: 1): 0.0253748 +HHA cross-socket operations ratio (Numa: 2): 0.329864 +HHA cross-socket operations ratio (Numa: 3): 0.18956 +``` + ### 采集系统调用函数耗时信息 libkperf基于tracepoint事件采集能力,在原有能力的基础上,重新封装了一组相关的调用API,来提供采集系统调用函数耗时信息的能力,类似于perf trace命令 diff --git a/docs/Go_API.md b/docs/Go_API.md index 30a5c3f..33d7a7c 100644 --- a/docs/Go_API.md +++ b/docs/Go_API.md @@ -346,6 +346,8 @@ func PmuDeviceOpen(attr []PmuDeviceAttr) (int, error) 初始化采集uncore事 * PMU_PCIE_TX_MRD_BW 采集pcie设备的tx方向上的读带宽,单位:Bytes/ns * PMU_PCIE_TX_MWR_BW 采集pcie设备的tx方向上的读带宽,单位:Bytes/ns * PMU_SMMU_TRAN 采集指定smmu设备的地址转换次数,单位:count + * PMU_HHA_CROSS_NUMA 采集每个numa的跨numa访问HHA的操作比例 + * PMU_HHA_CROSS_SOCKET 采集每个numa的跨socket访问HHA的操作比例 * Bdf: 指定需要采集设备的bdf号,只对pcie和smmu指标有效 * 返回值是int和error,pd > 0表示初始化成功,pd == -1初始化失败,可通过kperf.error()查看错误信息,以下是一个kperf.device_open的示例 diff --git a/docs/Python_API.md b/docs/Python_API.md index 2ec007a..ffebcb2 100644 --- a/docs/Python_API.md +++ b/docs/Python_API.md @@ -339,12 +339,14 @@ kperf.device_open(dev_attr: List[PmuDeviceAttr]) 初始化采集uncore事件指 * PMU_L3_TRAFFIC 采集每个core的L3的访问字节数,单位:Bytes * PMU_L3_MISS 采集每个core的L3的miss数量,单位:count * PMU_L3_REF 采集每个core的L3的总访问数量,单位:count - * PMU_L3_LAT 采集每个numa的L3的总时延,单位:cycles + * PMU_L3_LAT 采集每个cluster的L3的总时延,单位:cycles * PMU_PCIE_RX_MRD_BW 采集pcie设备的rx方向上的读带宽,单位:Bytes/ns * PMU_PCIE_RX_MWR_BW 采集pcie设备的rx方向上的写带宽,单位:Bytes/ns * PMU_PCIE_TX_MRD_BW 采集pcie设备的tx方向上的读带宽,单位:Bytes/ns * PMU_PCIE_TX_MWR_BW 采集pcie设备的tx方向上的读带宽,单位:Bytes/ns * PMU_SMMU_TRAN 采集指定smmu设备的地址转换次数,单位:count + * PMU_HHA_CROSS_NUMA 采集每个numa的跨numa访问HHA的操作比例 + * PMU_HHA_CROSS_SOCKET 采集每个numa的跨socket访问HHA的操作比例 * bdf: 指定需要采集设备的bdf号,只对pcie和smmu指标有效 * 返回值是int类型,pd > 0表示初始化成功,pd == -1初始化失败,可通过kperf.error()查看错误信息,以下是一个kperf.device_open的示例 diff --git a/example/llc_miss_ratio.cpp b/example/llc_miss_ratio.cpp deleted file mode 100644 index 5891f5d..0000000 --- a/example/llc_miss_ratio.cpp +++ /dev/null @@ -1,346 +0,0 @@ -/****************************************************************************** - * Copyright (c) Huawei Technologies Co., Ltd. 2025. 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: - * Create: 2025-05-13 - * Description: Collection capability for ddrc and l3c - * Current capability: Top-N thread sort of l3c_cache_miss ratio - ******************************************************************************/ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "pcerrc.h" -#include "pmu.h" -#include "symbol.h" - -static std::map numaTotalDDRC; //numa Id --> average ddrc bandwidth -static std::unordered_map numaToCpuCore; //numa Id --> cpu core ids -static std::unordered_map numaToCpuNumber; //numa Id --> length of cpu cores -static std::vector pidBoundCpus; //bounded cpus of designated pid -static unsigned numaNum = 0; //number of NUMAs - -const int FLOAT_PRECISION = 2; -const int TIME_UNIT_TRANS = 1000; - -uint64_t topNum = 0; -uint64_t duration = 0; -uint64_t period = 0; - -void totalDDRCBandwidth() -{ - PmuDeviceAttr devAttr[2]; - devAttr[0].metric = PMU_DDR_READ_BW; - devAttr[1].metric = PMU_DDR_WRITE_BW; - int pd = PmuDeviceOpen(devAttr, 2); - PmuEnable(pd); - sleep(1); - PmuData *oriData = nullptr; - int oriLen = PmuRead(pd, &oriData); - PmuDeviceData *devData = nullptr; - auto len = PmuGetDevMetric(oriData, oriLen, devAttr, 2, &devData); - std::unordered_map stats; - for (int i = 0; i < len; ++i) { - stats[devData[i].ddrNumaId] += devData[i].count / 1024 / 1024; - } - for (const auto &entry : stats) { - int id = entry.first; - double sum = entry.second; - numaTotalDDRC[id] = sum; - } - numaNum = numaTotalDDRC.size(); - DevDataFree(devData); - PmuDataFree(oriData); - PmuDisable(pd); -} - -// get numaId --> cpu core ids -void initNumaToCoreList() -{ - unsigned *coreList; - for (unsigned i = 0; i < numaNum; ++i) { - coreList = nullptr; - int len = PmuGetNumaCore(i, &coreList); - numaToCpuCore[i] = coreList; - numaToCpuNumber[i] = len; - } -} - -// parse the CPU core range in the format "0-255" or "0-3,5" -std::vector parseCpuRange(const std::string &rangeStr) -{ - std::vector cpus; - std::stringstream ss(rangeStr); - std::string part; - - while(getline(ss, part, ',')) { - size_t hyphen_pos = part.find("-"); - if (hyphen_pos != std::string::npos) { - int start = std::stoi(part.substr(0, hyphen_pos)); - int end = std::stoi(part.substr(hyphen_pos + 1)); - if (start > end) { - std::cerr << "Invalid CPU range: " << part << std::endl; - } - for (int i = start; i <= end; ++i) { - cpus.push_back(i); - } - } else { - cpus.push_back(std::stoi(part)); - } - } - - std::sort(cpus.begin(), cpus.end()); - cpus.erase(unique(cpus.begin(), cpus.end()), cpus.end()); - return cpus; -} - -// get cpu core of pid from /proc/[pid]/stat -std::string getCpuAffinityList(int pid) -{ - std::string path = "/proc/" + std::to_string(pid) + "/status"; - std::ifstream in(path); - if (!in.is_open()) { - std::cerr << "Not found: " << path << std::endl; - return ""; - } - std::string line; - const std::string targetKey = "Cpus_allowed_list:"; - while (getline(in, line)) { - if (line.find(targetKey) == 0) { - size_t pos = line.find("\t"); - if (pos == std::string::npos) - pos = targetKey.length(); - return line.substr(pos + 1); - } - } -} - -int getCpuCore(int pid) -{ - try { - std::string rangeStr = getCpuAffinityList(pid); - if (rangeStr == "") { - return -1; - } - pidBoundCpus = parseCpuRange(rangeStr); - } catch (const std::exception &e) { - std::cerr << "Error: " << e.what() << std::endl; - return 1; - } -} - -bool hasCommonCpu(const unsigned *cpuArray, size_t arraySize, const std::vector &cpuVector) -{ - if (cpuArray == nullptr || arraySize == 0 || cpuVector.empty()) { - return false; - } - - if (arraySize < cpuVector.size()) { - std::unordered_set arraySet(cpuArray, cpuArray + arraySize); - for (const auto &cpu : cpuVector) { - if (arraySet.count(cpu) > 0) { - return true; - } - } - } else { - std::unordered_set vecSet(cpuVector.begin(), cpuVector.end()); - for (size_t i = 0; i < arraySize; ++i) { - if (vecSet.count(cpuArray[i]) > 0) { - return true; - } - } - } - - return false; -} - -std::string GetL3CMissPercent(unsigned llc_miss, unsigned llc_cache) -{ - std::ostringstream oss; - double ratio = llc_cache != 0 ? static_cast(llc_miss) / llc_cache * 100.0 : 0.0; - oss << std::fixed << std::setprecision(FLOAT_PRECISION) << ratio; - return oss.str(); -} - - -void PrintHotSpotGraph(const std::unordered_map> tidData) -{ - std::vector>> sortedVec(tidData.begin(), tidData.end()); - std::sort(sortedVec.begin(), sortedVec.end(), [](const auto& a, const auto& b) { - double ratioA = (a.second.second == 0) ? 0.0 : static_cast(a.second.first) / a.second.second; - double ratioB = (b.second.second == 0) ? 0.0 : static_cast(b.second.first) / b.second.second; - return ratioA > ratioB; - }); - - std::cout << std::string(100, '=') << std::endl; - std::cout << std::string(100, '-') << std::endl; - std::cout << " " << std::setw(10) << " " << std::setw(20) << std::left << "Tid" << std::setw(20) << "llc_cache_miss" - << std::setw(20) << "llc_cache" << std::setw(20) << "llc_cache_miss_ratio" << std::endl; - std::cout << std::string(100, '-') << std::endl; - - size_t outputNum = std::min(topNum, tidData.size()); - for (int i = 0; i < outputNum; ++i) { - std::cout << " " << std::setw(10) << i << std::setw(20) << std::left << sortedVec[i].first << std::setw(20) - << sortedVec[i].second.first << std::setw(20) << sortedVec[i].second.second << std::setw(20) - << GetL3CMissPercent(sortedVec[i].second.first, sortedVec[i].second.second) + "%" << std::endl; - } - - std::cout << std::string(100, '_') << std::endl; -} - -int GetPmuDataHotspot(PmuData* pmuData, int pmuDataLen) -{ - if (pmuData == nullptr || pmuDataLen == 0) { - return SUCCESS; - } - - std::unordered_map> tidData; //tid --> (0x33, 0x32) - for (int i = 0; i < pmuDataLen; ++i) { - PmuData& data = pmuData[i]; - if (strcmp(data.evt, "r33") == 0) { - tidData[data.tid].first += data.count; - } - if (strcmp(data.evt, "r32") == 0) { - tidData[data.tid].second += data.count; - } - } - PrintHotSpotGraph(tidData); - return SUCCESS; -} - -void collectL3CMissRatio(int pid) { - char* evtList[2]; - evtList[0] = (char*)"r33"; - evtList[1] = (char*)"r32"; - PmuAttr attr = {0}; - attr.evtList = evtList; - attr.numEvt = 2; - attr.pidList = &pid; - attr.numPid = 1; - attr.cpuList = pidBoundCpus.data(); - attr.numCpu = pidBoundCpus.size(); - - int pd = PmuOpen(COUNTING, &attr); - if (pd == -1) { - std::cerr << "PmuOpen failed" << std::endl; - std::cerr << "error msg:" << Perror() << std::endl; - return; - } - - PmuEnable(pd); - int collectTimes = duration * TIME_UNIT_TRANS / period; - for (int i = 0; i < collectTimes; ++i) { - usleep(period * TIME_UNIT_TRANS); - PmuData* pmuData = nullptr; - int len = PmuRead(pd, &pmuData); - if (len == -1) { - std::cerr << "error msg:" << Perror() << std::endl; - return; - } - GetPmuDataHotspot(pmuData, len); - PmuDataFree(pmuData); - } - PmuDisable(pd); - PmuClose(pd); - return; -} - -// g++ -o llc_miss_ratio llc_miss_ratio.cpp -I ./output/include/ -L ./output/lib/ -lkperf -lsym -// export LD_LIBRARY_PATH=/XXX/libkperf/output/lib/:$LD_LIBRARY_PATH -void print_usage() { - std::cerr << "Usage: llc_miss_ratio \n"; - std::cerr << "--threshold : the collect threshold of total ddrc bandwidth, unit M/s\n"; - std::cerr << "--topNum : the top N thread of llc miss ratio collection\n"; - std::cerr << "--duration : the total collect time of llc_miss_ratio, unit s\n"; - std::cerr << "--period : the period of llc_miss_ratio collect, unit ms\n"; - std::cerr << " example: llc_miss_ratio 100 10 10 1000 \n"; -} - -int main(int argc, char** argv) -{ - if (argc < 5) { - print_usage(); - return 0; - } - double threshold = 0.0; - int pid = 0; - bool collectL3CMissFlag = false; - - try { - threshold = std::stod(argv[1]); - if (threshold <= 0) { - throw std::invalid_argument("threshold must be a positive number."); - } - - topNum = std::stod(argv[2]); - if (topNum <= 0) { - throw std::invalid_argument("TopNum must be a positive number."); - } - - duration = std::stod(argv[3]); - if (duration <= 0) { - throw std::invalid_argument("Duration must be a positive number."); - } - - period = std::stoi(argv[4]); - if (period <= 0) { - throw std::invalid_argument("Period must be a positive integer."); - } - - try { - pid = std::stoi(argv[5]); - } catch (const std::invalid_argument& e) { - std::cerr << "Not valid process id: " << e.what() << "\n"; - } - } catch (const std::exception& e) { - std::cerr << "Error parsing arguments: " << e.what() << "\n"; - print_usage(); - return EXIT_FAILURE; - } - - totalDDRCBandwidth(); - initNumaToCoreList(); - if(getCpuCore(pid) == -1) { - return EXIT_FAILURE; - } - - for (const auto &data : numaTotalDDRC) { - std::cout << "Numa ID: " << data.first << ", total bandwidth: " << data.second << "M/s"; - // bandwidth of numa greater than the threshold, check whether bounded cpus of pid correspond to this numa cores - if (data.second > threshold) { - auto cpuCoreList = numaToCpuCore[data.first]; - if (hasCommonCpu(cpuCoreList, numaToCpuNumber[data.first], pidBoundCpus)) { - std::cout << " --> exceed threshold, and the process is running on this numa"; - collectL3CMissFlag = true; - } else { - std::cout << " --> exceed threshold, the process is not running on this numa"; - } - } else { - std::cout << " --> not exceed threshold"; - } - std::cout << std::endl; - } - - if (collectL3CMissFlag) { - collectL3CMissRatio(pid); //begin to collect llc_miss and llc_cache event - } - - return 0; -} \ No newline at end of file diff --git a/go/src/libkperf/kperf/kperf.go b/go/src/libkperf/kperf/kperf.go index b21f304..9d84290 100644 --- a/go/src/libkperf/kperf/kperf.go +++ b/go/src/libkperf/kperf/kperf.go @@ -245,12 +245,12 @@ var ( // PmuDeviceMetric var ( - // Pernuma metric. - // Collect ddr read bandwidth for each numa node. + // Perchannel metric. + // Collect ddr read bandwidth for each channel. // Unit: Bytes/s PMU_DDR_READ_BW C.enum_PmuDeviceMetric = C.PMU_DDR_READ_BW - // Pernuma metric. - // Collect ddr write bandwidth for each numa node. + // Perchannel metric. + // Collect ddr write bandwidth for each channel. // Unit: Bytes/s PMU_DDR_WRITE_BW C.enum_PmuDeviceMetric = C.PMU_DDR_WRITE_BW // Percore metric. @@ -265,8 +265,8 @@ var ( // Collect L3 total reference count, including miss and hit count. // Unit: count PMU_L3_REF C.enum_PmuDeviceMetric = C.PMU_L3_REF - // Pernuma metric. - // Collect L3 total latency for each numa node. + // Percluster metric. + // Collect L3 total latency for each cluster node. // Unit: cycles PMU_L3_LAT C.enum_PmuDeviceMetric = C.PMU_L3_LAT // Collect pcie rx bandwidth. @@ -284,6 +284,12 @@ var ( // Collect smmu address transaction. // Unit: count PMU_SMMU_TRAN C.enum_PmuDeviceMetric = C.PMU_SMMU_TRAN + // Pernuma metric. + // Collect rate of cross-numa operations received by HHA. + PMU_HHA_CROSS_NUMA C.enum_PmuDeviceMetric = C.PMU_HHA_CROSS_NUMA + // Pernuma metric. + // Collect rate of cross-socket operations received by HHA. + PMU_HHA_CROSS_SOCKET C.enum_PmuDeviceMetric = C.PMU_HHA_CROSS_SOCKET ) // PmuBdfType diff --git a/include/pmu.h b/include/pmu.h index 1063cdb..3d9d684 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -419,12 +419,12 @@ int PmuGetField(struct SampleRawData *rawData, const char *fieldName, void *valu struct SampleRawField *PmuGetFieldExp(struct SampleRawData *rawData, const char *fieldName); enum PmuDeviceMetric { - // Pernuma metric. - // Collect ddr read bandwidth for each numa node. + // Perchannel metric. + // Collect ddr read bandwidth for each channel. // Unit: Bytes PMU_DDR_READ_BW, - // Pernuma metric. - // Collect ddr write bandwidth for each numa node. + // Perchannel metric. + // Collect ddr write bandwidth for each channel. // Unit: Bytes PMU_DDR_WRITE_BW, // Percore metric. @@ -457,7 +457,13 @@ enum PmuDeviceMetric { // Perpcie metric. // Collect smmu address transaction. // Unit: count - PMU_SMMU_TRAN + PMU_SMMU_TRAN, + // Pernuma metric. + // Collect rate of cross-numa operations received by HHA. + PMU_HHA_CROSS_NUMA, + // Pernuma metric. + // Collect rate of cross-socket operations received by HHA. + PMU_HHA_CROSS_SOCKET }; struct PmuDeviceAttr { diff --git a/pmu/pmu_metric.cpp b/pmu/pmu_metric.cpp index 66c70c4..1fe04d7 100644 --- a/pmu/pmu_metric.cpp +++ b/pmu/pmu_metric.cpp @@ -84,11 +84,13 @@ namespace KUNPENG_PMU { {PmuDeviceMetric::PMU_PCIE_RX_MWR_BW, "PMU_PCIE_RX_MWR_BW"}, {PmuDeviceMetric::PMU_PCIE_TX_MRD_BW, "PMU_PCIE_TX_MRD_BW"}, {PmuDeviceMetric::PMU_PCIE_TX_MWR_BW, "PMU_PCIE_TX_MWR_BW"}, - {PmuDeviceMetric::PMU_SMMU_TRAN, "PMU_SMMU_TRAN"} + {PmuDeviceMetric::PMU_SMMU_TRAN, "PMU_SMMU_TRAN"}, + {PmuDeviceMetric::PMU_HHA_CROSS_NUMA, "PMU_HHA_CROSS_NUMA"}, + {PmuDeviceMetric::PMU_HHA_CROSS_SOCKET, "PMU_HHA_CROSS_SOCKET"}, }; set percoreMetric = {PMU_L3_TRAFFIC, PMU_L3_MISS, PMU_L3_REF}; - set pernumaMetric = {PMU_L3_LAT}; + set pernumaMetric = {PMU_HHA_CROSS_NUMA, PMU_HHA_CROSS_SOCKET}; set perClusterMetric = {PMU_L3_LAT}; set perChannelMetric = {PMU_DDR_READ_BW, PMU_DDR_WRITE_BW}; set perpcieMetric = {PMU_PCIE_RX_MRD_BW, @@ -269,6 +271,30 @@ namespace KUNPENG_PMU { 2 } }; + + PMU_METRIC_PAIR HHA_CROSS_NUMA = { + PmuDeviceMetric::PMU_HHA_CROSS_NUMA, + { + "hisi_sccl", + "hha", + {"0x0", "0x02"}, + "", + "", + 0 + } + }; + + PMU_METRIC_PAIR HHA_CROSS_SOCKET = { + PmuDeviceMetric::PMU_HHA_CROSS_SOCKET, + { + "hisi_sccl", + "hha", + {"0x0", "0x01"}, + "", + "", + 0 + } + }; } static const map HIP_A_UNCORE_METRIC_MAP { @@ -278,6 +304,8 @@ namespace KUNPENG_PMU { METRIC_CONFIG::L3_MISS, METRIC_CONFIG::L3_REF, METRIC_CONFIG::SMMU_TRAN, + METRIC_CONFIG::HHA_CROSS_NUMA, + METRIC_CONFIG::HHA_CROSS_SOCKET, }; static const map HIP_B_UNCORE_METRIC_MAP { @@ -292,6 +320,8 @@ namespace KUNPENG_PMU { METRIC_CONFIG::PCIE_TX_MRD_BW, METRIC_CONFIG::PCIE_TX_MWR_BW, METRIC_CONFIG::SMMU_TRAN, + METRIC_CONFIG::HHA_CROSS_NUMA, + METRIC_CONFIG::HHA_CROSS_SOCKET, }; const UNCORE_METRIC_MAP UNCORE_METRIC_CONFIG_MAP = { @@ -852,7 +882,7 @@ namespace KUNPENG_PMU { } // remove duplicate device attribute - static int RemoveDupDeviceAttr(struct PmuDeviceAttr *attr, unsigned len, std::vector& deviceAttr, bool l3ReDup) + static int RemoveDupDeviceAttr(struct PmuDeviceAttr *attr, unsigned len, std::vector& deviceAttr) { std::unordered_set uniqueSet; for (int i = 0; i < len; ++i) { @@ -864,17 +894,6 @@ namespace KUNPENG_PMU { } if (uniqueSet.find(key) == uniqueSet.end()) { - // when in deviceopen remove the same PMU_L3_TRAFFIC and PMU_L3_REF, - // but when getDevMetric we need to keep them. - if (l3ReDup == true && - (attr[i].metric == PmuDeviceMetric::PMU_L3_TRAFFIC || attr[i].metric == PmuDeviceMetric::PMU_L3_REF)) { - if (uniqueSet.find(std::to_string(PmuDeviceMetric::PMU_L3_TRAFFIC)) != uniqueSet.end()) { - continue; - } - if (uniqueSet.find(std::to_string(PmuDeviceMetric::PMU_L3_REF)) != uniqueSet.end()) { - continue; - } - } uniqueSet.insert(key); deviceAttr.emplace_back(attr[i]); } @@ -957,6 +976,9 @@ namespace KUNPENG_PMU { case PMU_PCIE_TX_MWR_BW: case PMU_SMMU_TRAN: return PMU_METRIC_BDF; + case PMU_HHA_CROSS_NUMA: + case PMU_HHA_CROSS_SOCKET: + return PMU_METRIC_NUMA; } return PMU_METRIC_INVALID; } @@ -977,25 +999,57 @@ namespace KUNPENG_PMU { int AggregateByNuma(const PmuDeviceMetric metric, const vector &rawData, vector &devData) { - map devDataByNuma; + const auto& deviceConfig = GetDeviceMtricConfig(); + const auto& findConfig = deviceConfig.find(metric); + if (findConfig == deviceConfig.end()) { + return SUCCESS; + } + auto &evts = findConfig->second.events; + if (evts.size() != 2) { + return SUCCESS; + } + // Event name for total access count. + string totalEvt = evts[0]; + // Event name for cross-numa/cross-socket count. + string crossEvt = evts[1]; + // Sort data by numa, and then sort by event string. + map> devDataByNuma; for (auto &data : rawData) { + string devName; + string evtName; + if (!GetDeviceName(data.evtName, devName, evtName)) { + continue; + } + auto evtConfig = ExtractEvtStr("config", evtName); auto findData = devDataByNuma.find(data.numaId); if (findData == devDataByNuma.end()) { - PmuDeviceData outData; - outData.metric = data.metric; - outData.count = data.count; - outData.mode = GetMetricMode(data.metric); - outData.numaId = data.numaId; - devDataByNuma[data.numaId] = outData; + devDataByNuma[data.numaId][evtConfig] = data; } else { - findData->second.count += data.count; + devDataByNuma[data.numaId][evtConfig].count += data.count; } } for (auto &data : devDataByNuma) { - devData.push_back(data.second); + // Get events of cross-numa/cross-socket access count and total access count. + auto findcrossData = data.second.find(crossEvt); + auto findtotalData = data.second.find(totalEvt); + if (findcrossData == data.second.end() || findtotalData == data.second.end()) { + continue; + } + // Compute ratio: cross access count / total access count + double ratio = 0.0; + if (findtotalData->second.count != 0) { + ratio = (double)(findcrossData->second.count) / findtotalData->second.count; + } else { + ratio = -1; + } + PmuDeviceData outData; + outData.metric = metric; + outData.count = ratio; + outData.mode = GetMetricMode(metric); + outData.numaId = data.first; + devData.push_back(outData); } - return SUCCESS; } @@ -1264,6 +1318,8 @@ namespace KUNPENG_PMU { {PMU_PCIE_TX_MRD_BW, PcieBWAggregate}, {PMU_PCIE_TX_MWR_BW, PcieBWAggregate}, {PMU_SMMU_TRAN, SmmuTransAggregate}, + {PMU_HHA_CROSS_NUMA, AggregateByNuma}, + {PMU_HHA_CROSS_SOCKET, AggregateByNuma}, }; static bool IsMetricEvent(const string &devName, const string &evtName, const PmuDeviceAttr &devAttr) @@ -1366,7 +1422,7 @@ namespace KUNPENG_PMU { if (perClusterMetric.find(devAttr.metric) != perClusterMetric.end()) { devData.clusterId = pmuData[i].cpuTopo->coreId / clusterWidth; } - if (perChannelMetric.find(devAttr.metric) != pernumaMetric.end()) { + if (perChannelMetric.find(devAttr.metric) != perChannelMetric.end()) { devData.ddrNumaId = pmuData[i].cpuTopo->numaId; devData.socketId = pmuData[i].cpuTopo->socketId; } @@ -1454,7 +1510,7 @@ int PmuDeviceOpen(struct PmuDeviceAttr *attr, unsigned len) } // Remove duplicate device attributes. vector deviceAttr; - if (RemoveDupDeviceAttr(attr, len, deviceAttr, true) != SUCCESS) { + if (RemoveDupDeviceAttr(attr, len, deviceAttr) != SUCCESS) { return -1; } vector configEvtList; @@ -1466,8 +1522,17 @@ int PmuDeviceOpen(struct PmuDeviceAttr *attr, unsigned len) configEvtList.insert(configEvtList.end(), temp.begin(), temp.end()); } - vector evts; + //remove the same event of PMU_L3_TRAFFIC and PMU_L3_REF, PMU_HHA_CROSS_NUMA and PMU_HHA_CROSS_SOCKET + unordered_set tmpEvents; + vector filteredEvtList; for (auto& evt : configEvtList) { + if (tmpEvents.find(evt) == tmpEvents.end()) { + tmpEvents.insert(evt); + filteredEvtList.push_back(evt); + } + } + vector evts; + for (auto& evt : filteredEvtList) { evts.push_back(const_cast(evt.c_str())); } @@ -1519,7 +1584,7 @@ int PmuGetDevMetric(struct PmuData *pmuData, unsigned len, } // Remove duplicate device attributes. vector deviceAttr; - if (RemoveDupDeviceAttr(attr, attrLen, deviceAttr, false) != SUCCESS) { + if (RemoveDupDeviceAttr(attr, attrLen, deviceAttr) != SUCCESS) { return -1; } // Filter pmuData by metric and generate InnerDeviceData, diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index 2f0a2b5..213125d 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -107,12 +107,12 @@ class SymbolMode: RESOLVE_ELF_DWARF = 2 # Resolve elf and dwarf. All fields in Symbol will be valid. class PmuDeviceMetric: - # Pernuma metric. - # Collect ddr read bandwidth for each numa node. + # Perchannel metric. + # Collect ddr read bandwidth for each channel. # Unit: Bytes/s PMU_DDR_READ_BW = 0 - # Pernuma metric. - # Collect ddr write bandwidth for each numa node. + # Perchannel metric. + # Collect ddr write bandwidth for each channel. # Unit: Bytes/s PMU_DDR_WRITE_BW = 1 # Percore metric. @@ -127,8 +127,8 @@ class PmuDeviceMetric: # Collect L3 total reference count, including miss and hit count. # Unit: count PMU_L3_REF = 4 - # Pernuma metric. - # Collect L3 total latency for each numa node. + # Percluster metric. + # Collect L3 total latency for each cluster node. # Unit: cycles PMU_L3_LAT = 5 # Collect pcie rx bandwidth. @@ -146,6 +146,12 @@ class PmuDeviceMetric: # Collect smmu address transaction. # Unit: count PMU_SMMU_TRAN = 10 + # Pernuma metric. + # Collect rate of cross-numa operations received by HHA. + PMU_HHA_CROSS_NUMA = 11 + # Pernuma metric. + # Collect rate of cross-socket operations received by HHA. + PMU_HHA_CROSS_SOCKET = 12 class PmuDeviceAttr(_libkperf.PmuDeviceAttr): """ diff --git a/python/tests/test_metric.py b/python/tests/test_metric.py index bf653ce..90c254b 100644 --- a/python/tests/test_metric.py +++ b/python/tests/test_metric.py @@ -256,6 +256,28 @@ def test_get_metric_smmu_transaction(): print_dev_data_details(dev_data) kperf.close(pd) +def test_collect_hha_cross(): + dev_attr = [ + kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_HHA_CROSS_SOCKET), + kperf.PmuDeviceAttr(metric=kperf.PmuDeviceMetric.PMU_HHA_CROSS_NUMA) + ] + pd = kperf.device_open(dev_attr) + print(kperf.error()) + assert pd != -1, f"Expected non-negative pd, but got {pd}" + kperf.enable(pd) + time.sleep(1) + kperf.disable(pd) + ori_data = kperf.read(pd) + assert len(ori_data) != -1, f"Expected non-negative ori_len, but got {len(ori_data)}" + + dev_data = kperf.get_device_metric(ori_data, dev_attr) + assert dev_data[0].metric == kperf.PmuDeviceMetric.PMU_HHA_CROSS_SOCKET + assert dev_data[0].mode == kperf.PmuMetricMode.PMU_METRIC_NUMA + assert dev_data[-1].metric == kperf.PmuDeviceMetric.PMU_HHA_CROSS_NUMA + assert dev_data[-1].mode == kperf.PmuMetricMode.PMU_METRIC_NUMA + print_dev_data_details(dev_data) + kperf.close(pd) + if __name__ == '__main__': # 提示用户使用pytest 运行测试文件 print("This is a pytest script. Run it using the 'pytest' command.") diff --git a/test/test_perf/test_metric.cpp b/test/test_perf/test_metric.cpp index d10ca39..68710cb 100644 --- a/test/test_perf/test_metric.cpp +++ b/test/test_perf/test_metric.cpp @@ -309,4 +309,29 @@ TEST_F(TestMetric, GetMetricSmmuTransaction) DevDataFree(devData); PmuDataFree(oriData); PmuClose(pd); +} + +TEST_F(TestMetric, GetMetricHHACross) +{ + PmuDeviceAttr devAttr[2] = {}; + devAttr[0].metric = PMU_HHA_CROSS_NUMA; + devAttr[1].metric = PMU_HHA_CROSS_SOCKET; + int pd = PmuDeviceOpen(devAttr, 2); + ASSERT_NE(pd, -1); + PmuEnable(pd); + sleep(1); + PmuDisable(pd); + PmuData* oriData = nullptr; + int oriLen = PmuRead(pd, &oriData); + ASSERT_NE(oriLen, -1); + + PmuDeviceData *devData = nullptr; + auto len = PmuGetDevMetric(oriData, oriLen, devAttr, 2, &devData); + ASSERT_EQ(devData[0].metric, PMU_HHA_CROSS_NUMA); + ASSERT_EQ(devData[0].mode, PMU_METRIC_NUMA); + ASSERT_EQ(devData[len - 1].metric, PMU_HHA_CROSS_SOCKET); + ASSERT_EQ(devData[len - 1].mode, PMU_METRIC_NUMA); + DevDataFree(devData); + PmuDataFree(oriData); + PmuClose(pd); } \ No newline at end of file -- Gitee