From 5f617f50658b39a8042822bd260a8e6961ba4af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sat, 18 Jan 2025 10:10:20 +0800 Subject: [PATCH 01/10] add syscall elasped compute --- include/pcerrc.h | 2 + include/pmu.h | 85 +++++++++++++++++ pmu/pmu_analysis.cpp | 190 +++++++++++++++++++++++++++++++++++++ pmu/pmu_analysis.h | 59 ++++++++++++ pmu/pmu_trace_analysis.cpp | 190 +++++++++++++++++++++++++++++++++++++ 5 files changed, 526 insertions(+) create mode 100644 pmu/pmu_analysis.cpp create mode 100644 pmu/pmu_analysis.h create mode 100644 pmu/pmu_trace_analysis.cpp diff --git a/include/pcerrc.h b/include/pcerrc.h index e939bdc..b15b0eb 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -79,6 +79,8 @@ extern "C" { #define LIBPERF_ERR_COUNT_OVERFLOW 1035 #define LIBPERF_ERR_INVALID_GROUP_SPE 1036 #define LIBPERF_ERR_INVALID_GROUP_ALL_UNCORE 1037 +#define LIBPERF_ERR_INVALID_TRACE_TYPE 1038 +#define LIBPERF_ERR_INVALID_SYSCALL_FUN 1039 #define UNKNOWN_ERROR 9999 diff --git a/include/pmu.h b/include/pmu.h index d0b7e43..8c24bc3 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -133,6 +133,21 @@ struct PmuAttr { unsigned includeNewFork : 1; }; +enum PmuTraceType { + TRACE_SYS_CALL, +}; + +struct PmuTraceAttr { + // system call function List, if funcs is nullptr, it will collect all the system call function elapsed time. + const char **funcs; + // Length of system call function list + unsigned numFuncs; + int* pidList; + unsigned numPid; + int* cpuList; + unsigned numCpu; +}; + struct CpuTopology { int coreId; int numaId; @@ -190,6 +205,15 @@ struct PmuData { struct SampleRawData *rawData; // trace pointer collect data. }; +struct PmuTraceData { + const char *funcs; // system call function + double elapsedTime; // elapsed time + pid_t pid; // process id + int tid; // thread id + unsigned cpu; // cpu id + const char *comm; // process command +}; + /** * @brief * Initialize the collection target. @@ -328,6 +352,67 @@ int PmuGetField(struct SampleRawData *rawData, const char *fieldName, void *valu */ struct SampleRawField *PmuGetFieldExp(struct SampleRawData *rawData, const char *fieldName); +/** + * @brief + * Initialize the trace collection target. + * On success, a trace collect task id is returned which is the unique identity for the task. + * On error, -1 is returned. + * Refer to comments of PmuTraceAttr for details about settings. + * @param PmuTraceType task type + * @param PmuTraceAttr settings of the current trace collect task + * @return trace collect task id + */ +int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr); + +/** + * @brief + * Enable trace collection of task . + * On success, 0 is returned. + * On error, -1 is returned. + * @param pd trace collect task id + * @return error code + */ +int PmuTraceEnable(int pd); + +/** + * @brief + * Disable trace collection of task . + * On success, 0 is returned. + * On error, -1 is returned. + * @param pd trace collect task id + * @return error code + */ +int PmuTraceDisable(int pd); + +/** + * @brief + * Collect data. + * Pmu trace data are collected starting from the last PmuTraceEnable or PmuTraceRead. + * On success, length of data array is returned. + * If is NULL and the error code is 0, no data is available in the current collection time. + * If is NULL and the error code is not 0, an error occurs in the collection process and data cannot be read. + * @param pd trace collect task id + * @param PmuTraceData pmu trace data which is a pointer to an array + * @return length of pmu trace data + */ +int PmuTraceRead(int pd, struct PmuTraceData** pmuData); + +/** + * @brief + * Close task with id . + * After PmuTraceClose is called, all pmu trace data related to the task become invalid. + * @param pd trace collect task id + */ +void PmuTraceClose(int pd); + +/** + * @brief + * Query all available system call function from system. + * @param numFunc length of system call function list + * @return system call function list + */ +const char** PmuSysCallFuncList(unsigned *numFunc); + #pragma GCC visibility pop #ifdef __cplusplus } diff --git a/pmu/pmu_analysis.cpp b/pmu/pmu_analysis.cpp new file mode 100644 index 0000000..1dbbec1 --- /dev/null +++ b/pmu/pmu_analysis.cpp @@ -0,0 +1,190 @@ +/****************************************************************************** + * 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: Mr.Lei + * Create: 2025-01-17 + * Description: functions for analyze performance data in the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include "pmu_analysis.h" + +using namespace std; + +namespace KUNPENG_PMU { + const char *SYSCALL_FUNC_ENTER_PREFIX = "syscalls:sys_enter_"; + const char *SYSCALL_FUNC_EXIT_PREFIX = "syscalls:sys_exit_"; + + int PmuAnalysis::Register(const int pd, PmuTraceAttr* traceParam) + { + this->FillFuctionList(pd, traceParam); + return SUCCESS; + } + + void PmuAnalysis::FillFuctionList(unsigned pd, PmuTraceAttr* traceParam) + { + std::vector funcs; + for (int i = 0; i < traceParam->numFuncs; ++i) { + funcs.emplace_back(traceParam->funcs[i]); + } + funcsList[pd] = funcs; + } + bool CheckEventIsFunName(const char *evt, const char *funName) + { + if (const char *pos = strstr(evt, SYSCALL_FUNC_ENTER_PREFIX)) { + size_t evtFunLen = strlen(evt) - strlen(SYSCALL_FUNC_ENTER_PREFIX) + 1; + char *evtFunName = new char[evtFunLen]; + if (evtFunName == nullptr) { + return false; + } + strcpy(evtFunName, pos + strlen(SYSCALL_FUNC_ENTER_PREFIX)); + if (strcmp(evtFunName, funName) == 0) { + delete[] evtFunName; + return true; + } + delete[] evtFunName; + } + + if (const char *pos = strstr(evt, SYSCALL_FUNC_EXIT_PREFIX)) { + size_t evtFunLen = strlen(evt) - strlen(SYSCALL_FUNC_EXIT_PREFIX) + 1; + char *evtFunName = new char[evtFunLen]; + if (evtFunName == nullptr) { + return false; + } + strcpy(evtFunName, pos + strlen(SYSCALL_FUNC_EXIT_PREFIX)); + if (strcmp(evtFunName, funName) == 0) { + delete[] evtFunName; + return true; + } + delete[] evtFunName; + } + + return false; + } + + static bool CompareByTimeStamp(const PmuData &a, const PmuData &b) + { + return a.ts < b.ts; + } + + static void CollectPmuTraceData(const char *funName, const PmuData &enterPmuData, const PmuData &exitPmuData, vector &traceData) + { + PmuTraceData traceDataItem = {0}; + unsigned funLen = strlen(funName) + 1; + traceDataItem.funcs = new char[funLen]; + strcpy(traceDataItem.funcs, funName); + traceDataItem.elapsedTime = (double)(exitPmuData.ts - enterPmuData.ts) / 1000000.0; + traceDataItem.pid = enterPmuData.pid; + traceDataItem.tid = enterPmuData.tid; + traceDataItem.comm = enterPmuData.comm; + cout << "func: " << traceDataItem.funcs << " elapsed time: " << traceDataItem.elapsedTime << " ms." << endl; + + traceData.emplace_back(traceDataItem); + } + + static void printPmuData(vector &pmuData) + { + for (auto &data : pmuData) { + cout << "event_name: " << data.evt << "\tpid: " << data.pid << "\ttid: " << data.tid << "\tcpu: " << data.cpu << "\tts: " << data.ts + << "\tcomm: " << data.comm << "\tperiod: " << data.period << "\tcount: " << data.count << "\tcpuTopo: " << data.cpuTopo->coreId << " " + << data.cpuTopo->numaId << " " << data.cpuTopo->socketId << endl; + } + } + + static void printPmuTraceData(vector &traceData) + { + for (auto &data : traceData) { + cout << "func: " << data.funcs << "\tpid: " << data.pid << "\ttid: " << data.tid << "\tcpu: " << data.cpuTopo->coreId << " " + << data.cpuTopo->numaId << " " << data.cpuTopo->socketId << "\telapsed time: " << data.elapsedTime << " ms." << endl; + } + } + + int PmuAnalysis::AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len, PmuTraceData **pmuTraceData) + { + int oriDataLen = len; + vector& funList = funcsList.at(pd); + int oriLen = 0; + vector traceData; + for (int i = 0; i < funList.size(); ++i) { + map> tidPmuData; + string funName = funList[i]; + cout << "hanlde syscall Funcs: " << funName << endl; + for (; oriLen < oriDataLen; ++oriLen) { + if (!CheckEventIsFunName(pmuData[oriLen].evt, funName.c_str())) { + break; + } + tidPmuData[pmuData[oriLen].tid].emplace_back(pmuData[oriLen]); + } + + for (auto& tidPmuData : tidPmuData) { + cout << "tid: " << tidPmuData.first << endl; + vector enterList; + vector exitList; + for (int j = 0; j < tidPmuData.second.size(); ++j) { + if (strstr(tidPmuData.second[j].evt, SYSCALL_FUNC_ENTER_PREFIX)) { + enterList.emplace_back(tidPmuData.second[j]); + } else if (strstr(tidPmuData.second[j].evt, SYSCALL_FUNC_EXIT_PREFIX)) { + exitList.emplace_back(tidPmuData.second[j]); + } + } + if (enterList.size() == 0 || exitList.size() == 0) { + continue; + } + sort(enterList.begin(), enterList.end(), CompareByTimeStamp); + sort(exitList.begin(), exitList.end(), CompareByTimeStamp); + int enterIndex = 0; + int exitIndex = 0; + cout << "enterList size: " << enterList.size() << endl; + cout << "exitList size: " << exitList.size() << endl; + printPmuData(enterList); + printPmuData(exitList); + while (enterIndex < enterList.size() && exitIndex < exitList.size()) { + if (enterList[enterIndex].ts < exitList[exitIndex].ts) { + CollectPmuTraceData(funName.c_str(), enterList[enterIndex], exitList[exitIndex], traceData); + enterIndex++; + exitIndex++; + } else { + exitIndex++; + } + } + } + } + + TraceEventData newTraceData = { + .pd = pd, + .traceType = traceType, + .data = traceData, + }; + + auto inserted = traceDataList.emplace(newTraceData.data.data(), move(newTraceData)); + return inserted.first->second.data; + } + + void PmuAnalysis::EraseTraceDataList(const unsigned pd) + { + lock_guard lg(traceDataListMutex); + for (auto iter = traceDataList.begin(); iter != traceDataList.end();) { + if (iter->second.pd == pd) { + for (int i = 0; i < iter->second.data.size(); ++i) { + delete[] iter->second.data[i].funcs; + } + iter = traceDataList.erase(iter); + } else { + ++iter; + } + } + } + + void PmuAnalysis::Close(const int pd) + { + EraseTraceDataList(pd); + } + +} \ No newline at end of file diff --git a/pmu/pmu_analysis.h b/pmu/pmu_analysis.h new file mode 100644 index 0000000..f526e25 --- /dev/null +++ b/pmu/pmu_analysis.h @@ -0,0 +1,59 @@ +/****************************************************************************** + * 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: Mr.Zhang + * Create: 2025-01-17 + * Description: definition of singleton class PmuAnalysis for analyze performance data in the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_ANALYSIS_H +#define PMU_ANALYSIS_H +#include +#include +#include +#include "pmu.h" +#include "pmu_list.h" + +namespace KUNPENG_PMU { + class PmuAnalysis { + public: + static PmuAnalysis* GetInstance() + { + static PmuAnalysis instance; + return &instance; + } + + int Register(const int pd, PmuTraceAttr* traceParam); + int AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len, PmuTraceData **pmuTraceData); + void Close(const int pd); + + private: + PmuAnalysis() + {} + PmuAnalysis(const PmuAnalysis&) = delete; + PmuAnalysis& operator=(const PmuAnalysis&) = delete; + ~PmuAnalysis() = delete; + + struct TraceEventData { + unsigned pd; + PmuTraceType traceType; + std::vector data; + } + + void FillFuctionList(unsigned pd, PmuTraceAttr* traceParam); + + std::unordered_map> funcsList; + + static std::mutex traceDataListMtx; + // Key: PmuTraceData raw point + // Value: TraceEventData + std::unordered_map traceDataList; + }; +} // namespace KUNPENG_PMU +#endif \ No newline at end of file diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp new file mode 100644 index 0000000..6f3a76a --- /dev/null +++ b/pmu/pmu_trace_analysis.cpp @@ -0,0 +1,190 @@ +/****************************************************************************** + * 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: Mr.Zhang + * Create: 2025-01-17 + * Description: Pmu trace event analysis module. + * Current capability: Analyze the time consumed by system invoking function + ******************************************************************************/ +#include +#include +#include "pmu_list.h" +#include "pmu_analysis.h" +#include "pcerr.h" +#include "pmu.h" + +using namespace pcerr; +using namespace KUNPENG_PMU; +using namespace std; + +const char *SYSCALL_FUNC_ENTER_PREFIX = "syscalls:sys_enter_"; +const char *SYSCALL_FUNC_EXIT_PREFIX = "syscalls:sys_exit_"; +static vector SysCallFuncList; + +const char** PmuSysCallFuncList(unsigned *numFuncs) +{ + if (!SysCallFuncList.empty()) { + *numFuncs = SysCallFuncList.size(); + return SysCallFuncList.data(); + } + enum PmuEventType eventType = TRACE_EVENT; + unsigned int numTraceEvents = 0; + const char **traceEventList = PmuEventList(eventType, &numTraceEvents); + if (traceEventList == nullptr) { + return nullptr; + } + for (int i = 0; i < numTraceEvents; ++i) { + if (const char *pos = strstr(traceEventList[i], SYSCALL_FUNC_ENTER_PREFIX)) { + size_t funcNameLen = strlen(traceEventList[i]) - strlen(SYSCALL_FUNC_ENTER_PREFIX) + 1; + char *syscallFunName = new char[funcNameLen]; + if (syscallFunName == nullptr) { + return nullptr; + } + strcpy(syscallFunName, pos + strlen(SYSCALL_FUNC_ENTER_PREFIX)); + SysCallFuncList.emplace_back(syscallFunName); + } + } + + *numFuncs = SysCallFuncList.size(); + return SysCallFuncList.data(); +} + +static bool ValidateSysCallName(const char **funList, unsigned numFuns) +{ + const char **sysCallFuns = nullptr; + unsigned numSysCall = 0; + if (SysCallFuncList.empty()) { + sysCallFuns = PmuSysCallFuncList(&numSysCall); + } else { + sysCallFuns = SysCallFuncList.data(); + numSysCall = SysCallFuncList.size(); + } + for (int i = 0; i < numFuns; ++i) { + bool isValid = false; + for (int j = 0; j < numSysCall; ++j) { + if (sysCallFuns[j] != nullptr && funList[i] != nullptr && strcmp(sysCallFuns[j], funList[i]) == 0) { + isValid = true; + break; + } + } + if (!isValid) { + return false; + } + } + return true; +} + +static int CheckTraceAttr(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) +{ + if (traceType != TRACE_SYS_CALL) { + New(LIBPERF_ERR_INVALID_TRACE_TYPE, "traceType config error"); + return LIBPERF_ERR_INVALID_TRACE_TYPE; + } + if (traceAttr->funcs == nullptr) { + traceAttr->funcs = PmuSysCallFuncList(&traceAttr->numFuncs); + return SUCCESS; + } + + if (!ValidateSysCallName(traceAttr->funcs, traceAttr->numFuncs)) { + New(LIBPERF_ERR_INVALID_SYSCALL_FUN, "the system call function name error!"); + return LIBPERF_ERR_INVALID_SYSCALL_FUN; + } + + return SUCCESS; +} + +static char **GenerateSysCallFuncTraceEvtList(const char **sysCallFuncs, const unsigned numFuncs, unsigned int &numEvt) +{ + numEvt = 0; + char **syscallEvts = new char* [numFuncs * 2]; + for (size_t i = 0; i < numFuncs; ++i) { + size_t enterLen = strlen(SYSCALL_FUNC_ENTER_PREFIX) + strlen(sysCallFuncs[i]) + 1; + syscallEvts[numEvt] = new char[enterLen]; + strcpy(syscallEvts[numEvt], SYSCALL_FUNC_ENTER_PREFIX); + strcat(syscallEvts[numEvt], sysCallFuncs[i]); + numEvt++; + + size_t exitLen = strlen(SYSCALL_FUNC_EXIT_PREFIX) + strlen(sysCallFuncs[i]) + 1; + syscallEvts[numEvt] = new char[exitLen]; + strcpy(syscallEvts[numEvt], SYSCALL_FUNC_EXIT_PREFIX); + strcat(syscallEvts[numEvt], sysCallFuncs[i]); + numEvt++; + } + + return syscallEvts; +} + +int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) +{ + SetWarn(SUCCESS); + auto err = CheckTraceAttr(traceType, traceAttr); + if (err != SUCCESS) { + return -1; + } + err = KUNPENG_PMU::PmuAnalysis::GetInstance()->Register(pd, traceAttr); + if (err != SUCCESS) { + PmuAnalysis::GetInstance()->Close(pd); + return -1; + } + PmuAttr attr = {0}; + attr.evtList = GenerateSysCallFuncTraceEvtList(traceAttr->funcs, traceAttr->numFuncs, attr.numEvt); + attr.pidList = traceAttr->pidList; + attr.numPid = traceAttr->numPid; + attr.cpuList = traceAttr->cpuList; + attr.numCpu = traceAttr->numCpu; + attr.period = 1; // configured to sample once when an event occurs + + int ret = PmuOpen(SAMPLING, &attr); + for (int i = 0; i < attr.numEvt; ++i) { + delete[] attr.evtList[i]; + } + delete[] attr.evtList; + + return ret; +} + +int PmuTraceEnable(int pd) +{ + return PmuEnable(pd); +} + +int PmuTraceDisable(int pd) +{ + return PmuDisable(pd); +} + +int PmuTraceRead(int pd, struct PmuTraceData **pmuTraceData) +{ + PmuData *pmuData = nullptr; + unsigned len = PmuRead(pd, &pmuData); + if (len == -1) { + return -1; + } + unsigned traceLen = 0; + if (len > 0) { + traceLen = KUNPENG_PMU::PmuAnalysis::GetInstance()->AnalyzeTraceData(pd, pmuData, len, pmuTraceData); + } + + return traceLen; +} + +void PmuTraceClose(int pd) +{ + PmuClose(pd); + SetWarn(SUCCESS); + try { + KUNPENG_PMU::PmuAnalysis::GetInstance()->Close(pd); + New(SUCCESS); + } catch (std::bad_alloc&) { + New(COMMON_ERR_NOMEM); + } catch (std::exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + } +} -- Gitee From 599594b7942ce39e8d4386b8972fdf36e54d7633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sat, 18 Jan 2025 11:53:52 +0800 Subject: [PATCH 02/10] fix some complie bugs --- pmu/pmu_analysis.cpp | 29 +++++++++++++++++++++-------- pmu/pmu_analysis.h | 10 ++++++---- pmu/pmu_trace_analysis.cpp | 37 +++++++++++++++++++++++++------------ 3 files changed, 52 insertions(+), 24 deletions(-) diff --git a/pmu/pmu_analysis.cpp b/pmu/pmu_analysis.cpp index 1dbbec1..dce0558 100644 --- a/pmu/pmu_analysis.cpp +++ b/pmu/pmu_analysis.cpp @@ -14,11 +14,16 @@ ******************************************************************************/ #include #include +#include #include "pmu_analysis.h" using namespace std; namespace KUNPENG_PMU { + // Initializing PmuAnalysis singleton instance and global lock + std::mutex PmuAnalysis::funcsListMtx; + std::mutex PmuAnalysis::traceDataListMtx; + const char *SYSCALL_FUNC_ENTER_PREFIX = "syscalls:sys_enter_"; const char *SYSCALL_FUNC_EXIT_PREFIX = "syscalls:sys_exit_"; @@ -78,9 +83,10 @@ namespace KUNPENG_PMU { { PmuTraceData traceDataItem = {0}; unsigned funLen = strlen(funName) + 1; - traceDataItem.funcs = new char[funLen]; - strcpy(traceDataItem.funcs, funName); - traceDataItem.elapsedTime = (double)(exitPmuData.ts - enterPmuData.ts) / 1000000.0; + char *funcName = new char[funLen]; + strcpy(funcName, funName); + traceDataItem.funcs = funcName; + traceDataItem.elapsedTime = (double)(exitPmuData.ts - enterPmuData.ts) / 1000000.0; // convert to ms traceDataItem.pid = enterPmuData.pid; traceDataItem.tid = enterPmuData.tid; traceDataItem.comm = enterPmuData.comm; @@ -101,12 +107,12 @@ namespace KUNPENG_PMU { static void printPmuTraceData(vector &traceData) { for (auto &data : traceData) { - cout << "func: " << data.funcs << "\tpid: " << data.pid << "\ttid: " << data.tid << "\tcpu: " << data.cpuTopo->coreId << " " - << data.cpuTopo->numaId << " " << data.cpuTopo->socketId << "\telapsed time: " << data.elapsedTime << " ms." << endl; + cout << "func: " << data.funcs << "\tpid: " << data.pid << "\ttid: " << data.tid << "\tcpu: " << data.cpu + << "\telapsed time: " << data.elapsedTime << " ms." << endl; } } - int PmuAnalysis::AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len, PmuTraceData **pmuTraceData) + std::vector& PmuAnalysis::AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len) { int oriDataLen = len; vector& funList = funcsList.at(pd); @@ -159,7 +165,7 @@ namespace KUNPENG_PMU { TraceEventData newTraceData = { .pd = pd, - .traceType = traceType, + .traceType = TRACE_SYS_CALL, .data = traceData, }; @@ -167,9 +173,15 @@ namespace KUNPENG_PMU { return inserted.first->second.data; } + void PmuAnalysis::EraseFuncsList(const unsigned pd) + { + lock_guard lg(funcsListMtx); + funcsList.erase(pd); + } + void PmuAnalysis::EraseTraceDataList(const unsigned pd) { - lock_guard lg(traceDataListMutex); + lock_guard lg(traceDataListMtx); for (auto iter = traceDataList.begin(); iter != traceDataList.end();) { if (iter->second.pd == pd) { for (int i = 0; i < iter->second.data.size(); ++i) { @@ -184,6 +196,7 @@ namespace KUNPENG_PMU { void PmuAnalysis::Close(const int pd) { + EraseFuncsList(pd); EraseTraceDataList(pd); } diff --git a/pmu/pmu_analysis.h b/pmu/pmu_analysis.h index f526e25..e790555 100644 --- a/pmu/pmu_analysis.h +++ b/pmu/pmu_analysis.h @@ -30,7 +30,7 @@ namespace KUNPENG_PMU { } int Register(const int pd, PmuTraceAttr* traceParam); - int AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len, PmuTraceData **pmuTraceData); + std::vector& AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len); void Close(const int pd); private: @@ -38,18 +38,20 @@ namespace KUNPENG_PMU { {} PmuAnalysis(const PmuAnalysis&) = delete; PmuAnalysis& operator=(const PmuAnalysis&) = delete; - ~PmuAnalysis() = delete; + ~PmuAnalysis() = default; struct TraceEventData { unsigned pd; PmuTraceType traceType; std::vector data; - } + }; void FillFuctionList(unsigned pd, PmuTraceAttr* traceParam); - + void EraseFuncsList(const unsigned pd); + static std::mutex funcsListMtx; std::unordered_map> funcsList; + void EraseTraceDataList(const unsigned pd); static std::mutex traceDataListMtx; // Key: PmuTraceData raw point // Value: TraceEventData diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp index 6f3a76a..ba2e789 100644 --- a/pmu/pmu_trace_analysis.cpp +++ b/pmu/pmu_trace_analysis.cpp @@ -128,11 +128,6 @@ int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) if (err != SUCCESS) { return -1; } - err = KUNPENG_PMU::PmuAnalysis::GetInstance()->Register(pd, traceAttr); - if (err != SUCCESS) { - PmuAnalysis::GetInstance()->Close(pd); - return -1; - } PmuAttr attr = {0}; attr.evtList = GenerateSysCallFuncTraceEvtList(traceAttr->funcs, traceAttr->numFuncs, attr.numEvt); attr.pidList = traceAttr->pidList; @@ -141,13 +136,22 @@ int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) attr.numCpu = traceAttr->numCpu; attr.period = 1; // configured to sample once when an event occurs - int ret = PmuOpen(SAMPLING, &attr); + int pd = PmuOpen(SAMPLING, &attr); + if (pd == -1) { + return -1; + } + + err = KUNPENG_PMU::PmuAnalysis::GetInstance()->Register(pd, traceAttr); + if (err != SUCCESS) { + PmuAnalysis::GetInstance()->Close(pd); + return -1; + } for (int i = 0; i < attr.numEvt; ++i) { delete[] attr.evtList[i]; } delete[] attr.evtList; - return ret; + return pd; } int PmuTraceEnable(int pd) @@ -167,12 +171,21 @@ int PmuTraceRead(int pd, struct PmuTraceData **pmuTraceData) if (len == -1) { return -1; } - unsigned traceLen = 0; - if (len > 0) { - traceLen = KUNPENG_PMU::PmuAnalysis::GetInstance()->AnalyzeTraceData(pd, pmuData, len, pmuTraceData); + if (len == 0) { + *pmuTraceData = nullptr; + return 0; + } + New(SUCCESS); + auto& traceData = KUNPENG_PMU::PmuAnalysis::GetInstance()->AnalyzeTraceData(pd, pmuData, len); + if (!traceData.empty()) { + PmuDataFree(pmuData); // free other useless trace data. + *pmuTraceData = traceData.data(); + return traceData.size(); + } else { + PmuDataFree(pmuData); + *pmuTraceData = nullptr; + return 0; } - - return traceLen; } void PmuTraceClose(int pd) -- Gitee From 726c90e3cc4ad689f82444d0a556828eec5f5754 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sat, 18 Jan 2025 16:06:00 +0800 Subject: [PATCH 03/10] clean dup const string prefix --- pmu/pmu_analysis.h | 3 +++ pmu/pmu_trace_analysis.cpp | 16 ++++++++++------ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/pmu/pmu_analysis.h b/pmu/pmu_analysis.h index e790555..616363f 100644 --- a/pmu/pmu_analysis.h +++ b/pmu/pmu_analysis.h @@ -21,6 +21,9 @@ #include "pmu_list.h" namespace KUNPENG_PMU { + extern const char *SYSCALL_FUNC_ENTER_PREFIX; + extern const char *SYSCALL_FUNC_EXIT_PREFIX; + class PmuAnalysis { public: static PmuAnalysis* GetInstance() diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp index ba2e789..b153a02 100644 --- a/pmu/pmu_trace_analysis.cpp +++ b/pmu/pmu_trace_analysis.cpp @@ -24,8 +24,6 @@ using namespace pcerr; using namespace KUNPENG_PMU; using namespace std; -const char *SYSCALL_FUNC_ENTER_PREFIX = "syscalls:sys_enter_"; -const char *SYSCALL_FUNC_EXIT_PREFIX = "syscalls:sys_exit_"; static vector SysCallFuncList; const char** PmuSysCallFuncList(unsigned *numFuncs) @@ -121,6 +119,14 @@ static char **GenerateSysCallFuncTraceEvtList(const char **sysCallFuncs, const u return syscallEvts; } +static void EraseTraceAttrEvtList(char **evtList, unsigned numEvt) +{ + for (size_t i = 0; i < numEvt; ++i) { + delete[] evtList[i]; + } + delete[] evtList; +} + int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) { SetWarn(SUCCESS); @@ -138,18 +144,16 @@ int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) int pd = PmuOpen(SAMPLING, &attr); if (pd == -1) { + EraseTraceAttrEvtList(attr.evtList, attr.numEvt); return -1; } err = KUNPENG_PMU::PmuAnalysis::GetInstance()->Register(pd, traceAttr); + EraseTraceAttrEvtList(attr.evtList, attr.numEvt); if (err != SUCCESS) { PmuAnalysis::GetInstance()->Close(pd); return -1; } - for (int i = 0; i < attr.numEvt; ++i) { - delete[] attr.evtList[i]; - } - delete[] attr.evtList; return pd; } -- Gitee From 1d553e4a5d268a30286c3eacc9f64cef44a7ee55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sat, 18 Jan 2025 16:07:07 +0800 Subject: [PATCH 04/10] support system call collect python interface --- python/modules/_libkperf/Pmu.py | 364 ++++++++++++++++++++++++++++++++ python/modules/kperf/perror.py | 2 + python/modules/kperf/pmu.py | 76 +++++++ 3 files changed, 442 insertions(+) diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index c743d7d..cfe0da6 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -372,6 +372,131 @@ class PmuAttr: return pmu_attr +class CtypesPmuTraceAttr(ctypes.Structure): + """ + struct PmuTraceAttr { + // system call function List, if funcs is nullptr, it will collect all the system call function elapsed time. + const char **funcs; + // Length of system call function list + unsigned numFuncs; + int* pidList; + unsigned numPid; + int* cpuList; + unsigned numCpu; + }; + """ + _fields_ = [ + ('funcs', ctypes.POINTER(ctypes.c_char_p)), + ('numFuncs', ctypes.c_uint), + ('pidList', ctypes.POINTER(ctypes.c_int)), + ('numPid', ctypes.c_uint), + ('cpuList', ctypes.POINTER(ctypes.c_int)), + ('numCpu', ctypes.c_uint), + ] + + def __init__(self, + funcs: List[str]=None, + pidList: List[int]=None, + cpuList: List[int]=None, + *args: Any, **kw:Any) -> None: + super().__init__(*args, **kw) + + if funcs: + numFuncs = len(funcs) + self.funcs = (ctypes.c_char_p * numFuncs)(*[func.encode(UTF_8) for func in funcs]) + self.numFuncs = ctypes.c_uint(numFuncs) + else: + self.funcs = None + self.numFuncs = ctypes.c_uint(0) + + if pidList: + numPid = len(pidList) + self.pidList = (ctypes.c_int * numPid)(*pidList) + self.numPid = ctypes.c_uint(numPid) + else: + self.pidList = None + self.numPid = ctypes.c_uint(0) + + if cpuList: + numCpu = len(cpuList) + self.cpuList = (ctypes.c_int * numCpu)(*cpuList) + self.numCpu = ctypes.c_uint(numCpu) + else: + self.cpuList = None + self.numCpu = ctypes.c_uint(0) + + +class PmuTraceAttr: + __slots__ = ['__c_pmu_trace_attr'] + + def __init__(self, + funcs: List[str]=None, + pidList: List[int]=None, + cpuList: List[int]=None) -> None: + self.__c_pmu_trace_attr = CtypesPmuTraceAttr( + funcs=funcs, + pidList=pidList, + cpuList=cpuList + ) + + @property + def c_pmu_trace_attr(self) -> CtypesPmuTraceAttr: + return self.__c_pmu_trace_attr + + @property + def numFuncs(self) -> int: + return self.c_pmu_trace_attr.numFuncs + + @property + def funcs(self) -> List[str]: + return [self.c_pmu_trace_attr.funcs[i].decode(UTF_8) for i in range(self.numFuncs)] + + @funcs.setter + def funcs(self, funcs: List[str]) -> None: + if funcs: + numFuncs = len(funcs) + self.c_pmu_trace_attr.funcs = (ctypes.c_char_p * numFuncs)(*[func.encode(UTF_8) for func in funcs]) + self.c_pmu_trace_attr.numFuncs = ctypes.c_uint(numFuncs) + else: + self.c_pmu_trace_attr.funcs = None + self.c_pmu_trace_attr.numFuncs = ctypes.c_uint(0) + + @property + def numPid(self) -> int: + return self.c_pmu_trace_attr.numPid + + @property + def pidList(self) -> List[int]: + return [self.c_pmu_trace_attr.pidList[i] for i in range(self.numPid)] + + @pidList.setter + def pidList(self, pidList: List[int]) -> None: + if pidList: + numPid = len(pidList) + self.c_pmu_trace_attr.pidList = (ctypes.c_int * numPid)(*[pid for pid in pidList]) + self.c_pmu_trace_attr.numPid = ctypes.c_uint(numPid) + else: + self.c_pmu_trace_attr.pidList = None + self.c_pmu_trace_attr.numPid = ctypes.c_uint(0) + + @property + def numCpu(self) -> int: + return self.c_pmu_trace_attr.numCpu + + @property + def cpuList(self) -> List[int]: + return [self.c_pmu_trace_attr.cpuList[i] for i in range(self.numCpu)] + + @cpuList.setter + def cpuList(self, cpuList: List[int]) -> None: + if cpuList: + numCpu = len(cpuList) + self.c_pmu_trace_attr.cpuList = (ctypes.c_int * numCpu)(*[cpu for cpu in cpuList]) + self.c_pmu_trace_attr.numCpu = ctypes.c_uint(numCpu) + else: + self.c_pmu_trace_attr.cpuList = None + self.c_pmu_trace_attr.numCpu = ctypes.c_uint(0) + class CtypesCpuTopology(ctypes.Structure): """ struct CpuTopology { @@ -860,6 +985,137 @@ class PmuData: PmuDataFree(self.__pointer) self.__pointer = None +class CtypesPmuTraceData(ctypes.Structure): + """ + struct PmuTraceData { + const char *funcs; // system call function + double elapsedTime; // elapsed time + pid_t pid; // process id + int tid; // thread id + unsigned cpu; // cpu id + const char *comm; // process command + }; + """ + _fields_ = [ + ('funcs', ctypes.POINTER(ctypes.c_char)), + ('elapsedTime', ctypes.c_double), + ('pid', ctypes.c_int), + ('tid', ctypes.c_int), + ('cpu', ctypes.c_uint), + ('comm', ctypes.POINTER(ctypes.c_char)) + ] + + def __init__(self, + funcs: str = None, + elapsedTime: float = 0.0, + pid: int = 0, + tid: int = 0, + cpu: int = 0, + comm: str = None, + *args: Any, **kw: Any) -> None: + super().__init__(*args, **kw) + + self.funcs = ctypes.c_char_p(funcs.encode(UTF_8)) + self.elapsedTime = ctypes.c_double(elapsedTime) + self.pid = ctypes.c_int(pid) + self.tid = ctypes.c_int(tid) + self.cpu = ctypes.c_uint(cpu) + self.comm = ctypes.c_char_p(comm.encode(UTF_8)) + +class ImplPmuTraceData: + __slots__ = ['__c_pmu_trace_data'] + def __init__(self, + funcs: str = None, + elapsedTime: float = 0.0, + pid: int = 0, + tid: int = 0, + cpu: int = 0, + comm: str = None, + *args: Any, **kw: Any) -> None: + self.__c_pmu_trace_data = CtypesPmuTraceData( + funcs=funcs, + elapsedTime=elapsedTime, + pid=pid, + tid=tid, + cpu=cpu, + comm=comm + ) + + @property + def c_pmu_trace_data(self) -> CtypesPmuTraceData: + return self.__c_pmu_trace_data + + @property + def funcs(self) -> str: + return self.__c_pmu_trace_data.funcs.decode(UTF_8) + + @funcs.setter + def funcs(self, funcs: str) -> None: + self.__c_pmu_trace_data.funcs = ctypes.c_char_p(funcs.encode(UTF_8)) + + @property + def elapsedTime(self) -> float: + return self.__c_pmu_trace_data.elapsedTime + + @elapsedTime.setter + def elapsedTime(self, elapsedTime: float) -> None: + self.__c_pmu_trace_data.elapsedTime = ctypes.c_double(elapsedTime) + + @property + def pid(self) -> int: + return self.__c_pmu_trace_data.pid + + @pid.setter + def pid(self, pid: int) -> None: + self.__c_pmu_trace_data.pid = ctypes.c_int(pid) + + @property + def tid(self) -> int: + return self.__c_pmu_trace_data.tid + + @tid.setter + def tid(self, tid: int) -> None: + self.__c_pmu_trace_data.tid = ctypes.c_int(tid) + + @property + def cpu(self) -> int: + return self.__c_pmu_trace_data.cpu + + @cpu.setter + def cpu(self, cpu: int) -> None: + self.__c_pmu_trace_data.cpu = ctypes.c_uint(cpu) + + @property + def comm(self) -> str: + return self.__c_pmu_trace_data.comm.decode(UTF_8) + + @comm.setter + def comm(self, comm: str) -> None: + self.__c_pmu_trace_data.comm = ctypes.c_char_p(comm.encode(UTF_8)) + +class PmuTraceData: + __slots__ = ['__pointer', '__iter', '__len'] + + def __init__(self, pointer: ctypes.POINTER(CtypesPmuTraceData) = None, len: int = 0) -> None: + self.__pointer = pointer + self.__len = len + self.__iter = (ImplPmuTraceData.from_c_pmu_trace_data(self.__pointer[i]) for i in range(self.__len)) + + def __del__(self) -> None: + self.free() + + @property + def len(self) -> int: + return self.__len + + @property + def iter(self) -> Iterator[ImplPmuTraceData]: + return self.__iter + + def free(self) -> None: + if self.__pointer is not None: + PmuTraceDataFree(self.__pointer) + self.__pointer = None def PmuOpen(collectType: int, pmuAttr: PmuAttr) -> int: """ @@ -1044,6 +1300,101 @@ def PmuGetFieldExp(rawData: ctypes.POINTER(CtypesSampleRawData), field_name: str return SampleRawField.from_sample_raw_field(pointer_field.contents) +def PmuTraceOpen(traceType: int, pmuTraceAttr: PmuTraceAttr) -> int: + """ + int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr); + """ + c_PmuTraceOpen = kperf_so.PmuTraceOpen + c_PmuTraceOpen.argtypes = [ctypes.c_int, ctypes.POINTER(CtypesPmuTraceAttr)] + c_PmuTraceOpen.restype = ctypes.c_int + + c_traceType = ctypes.c_int(traceType) + + return c_PmuTraceOpen(c_traceType, ctypes.byref(pmuTraceAttr.c_pmu_trace_attr)) + +def PmuTraceEnable(pd: int) -> int: + """ + int PmuTraceEnable(int pd); + """ + c_PmuTraceEnable = kperf_so.PmuTraceEnable + c_PmuTraceEnable.argtypes = [ctypes.c_int] + c_PmuTraceEnable.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + + return c_PmuTraceEnable(c_pd) + +def PmuTraceDisable(pd: int) -> int: + """ + int PmuTraceDisable(int pd); + """ + c_PmuTraceDisable = kperf_so.PmuTraceDisable + c_PmuTraceDisable.argtypes = [ctypes.c_int] + c_PmuTraceDisable.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + + return c_PmuTraceDisable(c_pd) + +def PmuTraceRead(pd: int) -> PmuTraceData: + """ + int PmuTraceRead(int pd, struct PmuTraceData** pmuTraceData); + """ + c_PmuTraceRead = kperf_so.PmuTraceRead + c_PmuTraceRead.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.POINTER(CtypesPmuTraceData))] + c_PmuTraceRead.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + c_data_pointer = ctypes.pointer(CtypesPmuTraceData()) + + c_data_len = c_PmuTraceRead(c_pd, ctypes.byref(c_data_pointer)) + return PmuTraceData(c_data_pointer, c_data_len) + +def PmuTraceClose(pd: int) -> None: + """ + void PmuTraceClose(int pd); + """ + c_PmuTraceClose = kperf_so.PmuTraceClose + c_PmuTraceClose.argtypes = [ctypes.c_int] + c_PmuTraceClose.restype = None + + c_pd = ctypes.c_int(pd) + + c_PmuTraceClose(c_pd) + +def PmuTraceDataFree(pmuTraceData: ctypes.POINTER(CtypesPmuTraceData)) -> None: + """ + void PmuTraceDataFree(struct PmuTraceData* pmuTraceData); + """ + c_PmuTraceDataFree = kperf_so.PmuTraceDataFree + c_PmuTraceDataFree.argtypes = [ctypes.POINTER(CtypesPmuTraceData)] + c_PmuTraceDataFree.restype = None + c_PmuTraceDataFree(pmuTraceData) + +def PmuSysCallFuncList() -> Iterator[str]: + """ + char **PmuSysCallFuncList(unsigned *numFunc); + """ + c_PmuSysCallFuncList = kperf_so.PmuSysCallFuncList + c_PmuSysCallFuncList.argtypes = [ctypes.c_int] + c_PmuSysCallFuncList.restype = ctypes.POINTER(ctypes.c_char_p) + + c_num_func = ctypes.c_int() + c_func_list = c_PmuSysCallFuncList(ctypes.byref(c_num_func)) + + return (c_func_list[i].decode(UTF_8) for i in range(c_num_func.value)) + +def PmuSysCallFuncListFree() -> None: + """ + void PmuSysCallFuncListFree(); + """ + c_PmuSysCallFuncListFree = kperf_so.PmuSysCallFuncListFree + c_PmuSysCallFuncListFree.argtypes = [] + c_PmuSysCallFuncListFree.restype = None + + c_PmuSysCallFuncListFree() + + __all__ = [ 'CtypesEvtAttr', 'EvtAttr', @@ -1066,4 +1417,17 @@ __all__ = [ 'PmuDumpData', 'PmuGetField', 'PmuGetFieldExp', + 'CtypesPmuTraceAttr', + 'PmuTraceAttr', + 'CtypesPmuTraceData', + 'ImplPmuTraceData', + 'PmuTraceData', + 'PmuTraceOpen', + 'PmuTraceEnable', + 'PmuTraceDisable', + 'PmuTraceRead', + 'PmuTraceClose', + 'PmuTraceDataFree', + 'PmuSysCallFuncList', + 'PmuSysCallFuncListFree', ] diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py index 097f1f6..ee35974 100644 --- a/python/modules/kperf/perror.py +++ b/python/modules/kperf/perror.py @@ -78,6 +78,8 @@ class Error: LIBPERF_ERR_COUNT_OVERFLOW = 1035 LIBPERF_ERR_INVALID_GROUP_SPE = 1036 LIBPERF_ERR_INVALID_GROUP_ALL_UNCORE = 1037 + LIBPERF_ERR_INVALID_TRACE_TYPE = 1038 + LIBPERF_ERR_INVALID_SYSCALL_FUN = 1039 UNKNOWN_ERROR = 9999 diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index d74fa8d..4d04864 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -33,6 +33,9 @@ class PmuEventType: ALL_EVENT = 3 +class PmuTraceType: + TRACE_SYS_CALL = 0 + class SpeFilter: SPE_FILTER_NONE = 0 TS_ENABLE = 1 << 0 # enable timestamping with value of generic timer @@ -172,7 +175,34 @@ class ImplPmuData(_libkperf.ImplPmuData): class PmuData(_libkperf.PmuData): pass +class PmuTraceAttr(_libkperf.PmuTraceAttr): + """ + struct PmuTraceAttr { + // system call function List, if funcs is nullptr, it will collect all the system call function elapsed time. + const char **funcs; + // Length of system call function list + unsigned numFuncs; + int* pidList; + unsigned numPid; + int* cpuList; + unsigned numCpu; + }; + """ + def __init__(self, + funcs: List[str] = None, + pidList: List[int] = None, + cpuList: List[int] = None) -> None: + super().__init__( + funcs=funcs, + pidList=pidList, + cpuList=cpuList + ) + +class ImplPmuTraceData(_libkperf.ImplPmuTraceData): + pass +class PmuTraceData(_libkperf.PmuTraceData): + pass def open(collect_type: PmuTaskType, pmu_attr: PmuAttr) -> int: """ @@ -282,10 +312,47 @@ def get_field_exp(pmu_data: _libkperf.ImplPmuData, field_name: str) -> SampleRaw """ return _libkperf.PmuGetFieldExp(pmu_data.rawData.c_pmu_data_rawData, field_name) +def trace_open(trace_type: PmuTraceType, pmu_trace_attr: PmuTraceAttr) -> int: + """ + int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr); + """ + return _libkperf.PmuTraceOpen(int(trace_type), pmu_trace_attr) + +def trace_enable(pd: int) -> int: + """ + int PmuTraceEnable(int pd); + """ + return _libkperf.PmuTraceEnable(pd) + +def trace_disable(pd: int) -> int: + """ + int PmuTraceDisable(int pd); + """ + return _libkperf.PmuTraceDisable(pd) + +def trace_read(pd: int) -> PmuTraceData: + """ + int PmuTraceRead(int pd, struct PmuTraceData **traceData); + """ + return _libkperf.PmuTraceRead(pd) + +def trace_close(pd: int) -> None: + """ + void PmuTraceClose(int pd); + """ + return _libkperf.PmuTraceClose(pd) + +def sys_call_func_list() -> Iterator[str]: + """ + get the system call function list + :return: system call function list + """ + return _libkperf.PmuSysCallFuncList() __all__ = [ 'PmuTaskType', 'PmuEventType', + 'PmuTraceType', 'SpeFilter', 'SpeEventFilter', 'SymbolMode', @@ -295,6 +362,9 @@ __all__ = [ 'SampleRawField', 'ImplPmuData', 'PmuData', + 'PmuTraceAttr', + 'ImplPmuTraceData', + 'PmuTraceData', 'open', 'event_list', 'enable', @@ -305,4 +375,10 @@ __all__ = [ 'dump', 'get_field', 'get_field_exp', + 'trace_open', + 'trace_enable', + 'trace_disable', + 'trace_read', + 'trace_close', + 'sys_call_func_list', ] -- Gitee From 91ed3f21aa927c9b2ca4153321a8282f8665070c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sat, 18 Jan 2025 17:17:49 +0800 Subject: [PATCH 05/10] add some mutex and add some free interface --- include/pmu.h | 6 ++++++ pmu/pmu_analysis.cpp | 33 +++++++++++++++++++++++++++++++-- pmu/pmu_analysis.h | 1 + pmu/pmu_trace_analysis.cpp | 21 +++++++++++++++++++++ 4 files changed, 59 insertions(+), 2 deletions(-) diff --git a/include/pmu.h b/include/pmu.h index 8c24bc3..7f41c18 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -405,6 +405,12 @@ int PmuTraceRead(int pd, struct PmuTraceData** pmuData); */ void PmuTraceClose(int pd); +/** + * @brief Free PmuTraceData pointer. + * @param pmuTraceData + */ +void PmuTraceDataFree(struct PmuTraceData* pmuTraceData); + /** * @brief * Query all available system call function from system. diff --git a/pmu/pmu_analysis.cpp b/pmu/pmu_analysis.cpp index dce0558..b6b3c42 100644 --- a/pmu/pmu_analysis.cpp +++ b/pmu/pmu_analysis.cpp @@ -35,12 +35,14 @@ namespace KUNPENG_PMU { void PmuAnalysis::FillFuctionList(unsigned pd, PmuTraceAttr* traceParam) { + lock_guard lg(funcsListMtx); std::vector funcs; for (int i = 0; i < traceParam->numFuncs; ++i) { funcs.emplace_back(traceParam->funcs[i]); } funcsList[pd] = funcs; } + bool CheckEventIsFunName(const char *evt, const char *funName) { if (const char *pos = strstr(evt, SYSCALL_FUNC_ENTER_PREFIX)) { @@ -89,7 +91,10 @@ namespace KUNPENG_PMU { traceDataItem.elapsedTime = (double)(exitPmuData.ts - enterPmuData.ts) / 1000000.0; // convert to ms traceDataItem.pid = enterPmuData.pid; traceDataItem.tid = enterPmuData.tid; - traceDataItem.comm = enterPmuData.comm; + unsigned commLen = strlen(enterPmuData.comm) + 1; + char *commName = new char[commLen]; + strcpy(commName, enterPmuData.comm); + traceDataItem.comm = commName; cout << "func: " << traceDataItem.funcs << " elapsed time: " << traceDataItem.elapsedTime << " ms." << endl; traceData.emplace_back(traceDataItem); @@ -114,6 +119,7 @@ namespace KUNPENG_PMU { std::vector& PmuAnalysis::AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len) { + lock_guard lg(traceDataListMtx); int oriDataLen = len; vector& funList = funcsList.at(pd); int oriLen = 0; @@ -185,7 +191,12 @@ namespace KUNPENG_PMU { for (auto iter = traceDataList.begin(); iter != traceDataList.end();) { if (iter->second.pd == pd) { for (int i = 0; i < iter->second.data.size(); ++i) { - delete[] iter->second.data[i].funcs; + if (iter->second.data[i].funcs != nullptr) { + delete[] iter->second.data[i].funcs; + } + if (iter->second.data[i].comm != nullptr) { + delete[] iter->second.data[i].comm; + } } iter = traceDataList.erase(iter); } else { @@ -194,6 +205,24 @@ namespace KUNPENG_PMU { } } + void PmuAnalysis::FreeTraceData(PmuTraceData* pmuTraceData) + { + lock_guard lg(traceDataListMtx); + auto findData = traceDataList.find(pmuTraceData); + if (findData == traceDataList.end()) { + return; + } + for (auto& data : findData->second.data) { + if (data.funcs != nullptr) { + delete[] data.funcs; + } + if (data.comm != nullptr) { + delete[] data.comm; + } + } + traceDataList.erase(pmuTraceData); + } + void PmuAnalysis::Close(const int pd) { EraseFuncsList(pd); diff --git a/pmu/pmu_analysis.h b/pmu/pmu_analysis.h index 616363f..eeed729 100644 --- a/pmu/pmu_analysis.h +++ b/pmu/pmu_analysis.h @@ -35,6 +35,7 @@ namespace KUNPENG_PMU { int Register(const int pd, PmuTraceAttr* traceParam); std::vector& AnalyzeTraceData(int pd, PmuData *pmuData, unsigned len); void Close(const int pd); + void FreeTraceData(PmuTraceData* pmuTraceData); private: PmuAnalysis() diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp index b153a02..7c5ca25 100644 --- a/pmu/pmu_trace_analysis.cpp +++ b/pmu/pmu_trace_analysis.cpp @@ -24,10 +24,12 @@ using namespace pcerr; using namespace KUNPENG_PMU; using namespace std; +static std::mutex SysCallListMtx; static vector SysCallFuncList; const char** PmuSysCallFuncList(unsigned *numFuncs) { + lock_guard lg(SysCallListMtx); if (!SysCallFuncList.empty()) { *numFuncs = SysCallFuncList.size(); return SysCallFuncList.data(); @@ -54,6 +56,17 @@ const char** PmuSysCallFuncList(unsigned *numFuncs) return SysCallFuncList.data(); } +void PmuSysCallFuncListFree() +{ + lock_guard lg(SysCallListMtx); + for (auto &funcName : SysCallFuncList) { + if (funcName != nullptr) { + delete[] funcName; + } + } + SysCallFuncList.clear(); +} + static bool ValidateSysCallName(const char **funList, unsigned numFuns) { const char **sysCallFuns = nullptr; @@ -198,6 +211,7 @@ void PmuTraceClose(int pd) SetWarn(SUCCESS); try { KUNPENG_PMU::PmuAnalysis::GetInstance()->Close(pd); + PmuSysCallFuncListFree(); New(SUCCESS); } catch (std::bad_alloc&) { New(COMMON_ERR_NOMEM); @@ -205,3 +219,10 @@ void PmuTraceClose(int pd) New(UNKNOWN_ERROR, ex.what()); } } + +void PmuTraceDataFree(struct PmuTraceData *pmuTraceData) +{ + SetWarn(SUCCESS); + KUNPENG_PMU::PmuAnalysis::GetInstance()->FreeTraceData(pmuTraceData); + New(SUCCESS); +} -- Gitee From 757036790c07cd8b68a2b64f6171531b4aea7efb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sat, 18 Jan 2025 18:29:02 +0800 Subject: [PATCH 06/10] fix soem python interface problem --- python/modules/_libkperf/Pmu.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index cfe0da6..83d0ffb 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -997,21 +997,21 @@ class CtypesPmuTraceData(ctypes.Structure): }; """ _fields_ = [ - ('funcs', ctypes.POINTER(ctypes.c_char)), + ('funcs', ctypes.c_char_p), ('elapsedTime', ctypes.c_double), ('pid', ctypes.c_int), ('tid', ctypes.c_int), ('cpu', ctypes.c_uint), - ('comm', ctypes.POINTER(ctypes.c_char)) + ('comm', ctypes.c_char_p) ] def __init__(self, - funcs: str = None, + funcs: str = '', elapsedTime: float = 0.0, pid: int = 0, tid: int = 0, cpu: int = 0, - comm: str = None, + comm: str = '', *args: Any, **kw: Any) -> None: super().__init__(*args, **kw) @@ -1025,12 +1025,12 @@ class CtypesPmuTraceData(ctypes.Structure): class ImplPmuTraceData: __slots__ = ['__c_pmu_trace_data'] def __init__(self, - funcs: str = None, + funcs: str = '', elapsedTime: float = 0.0, pid: int = 0, tid: int = 0, cpu: int = 0, - comm: str = None, + comm: str = '', *args: Any, **kw: Any) -> None: self.__c_pmu_trace_data = CtypesPmuTraceData( funcs=funcs, @@ -1092,6 +1092,12 @@ class ImplPmuTraceData: @comm.setter def comm(self, comm: str) -> None: self.__c_pmu_trace_data.comm = ctypes.c_char_p(comm.encode(UTF_8)) + + @classmethod + def from_c_pmu_trace_data(cls, c_pmu_trace_data: CtypesPmuTraceData) -> 'ImplPmuTraceData': + pmu_trace_data = cls() + pmu_trace_data.__c_pmu_trace_data = c_pmu_trace_data + return pmu_trace_data class PmuTraceData: __slots__ = ['__pointer', '__iter', '__len'] @@ -1132,7 +1138,7 @@ def PmuOpen(collectType: int, pmuAttr: PmuAttr) -> int: def PmuEventListFree() -> None: """ - int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr); + void PmuEventListFree(); """ c_PmuEventListFree = kperf_so.PmuEventListFree c_PmuEventListFree.argtypes = [] -- Gitee From b97ff4ef11a0e77f442693906dcb3d99a048f1c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sun, 19 Jan 2025 08:57:48 +0800 Subject: [PATCH 07/10] fix some error code call format and some try-catch --- include/pcerrc.h | 1 + pmu/pmu_analysis.h | 2 +- pmu/pmu_trace_analysis.cpp | 98 +++++++++++++++++++--------------- python/modules/kperf/perror.py | 1 + 4 files changed, 58 insertions(+), 44 deletions(-) diff --git a/include/pcerrc.h b/include/pcerrc.h index b15b0eb..28ee4ea 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -81,6 +81,7 @@ extern "C" { #define LIBPERF_ERR_INVALID_GROUP_ALL_UNCORE 1037 #define LIBPERF_ERR_INVALID_TRACE_TYPE 1038 #define LIBPERF_ERR_INVALID_SYSCALL_FUN 1039 +#define LIBPERF_ERR_QUERY_SYSCALL_LIST_FAILED 1040 #define UNKNOWN_ERROR 9999 diff --git a/pmu/pmu_analysis.h b/pmu/pmu_analysis.h index eeed729..904fcf5 100644 --- a/pmu/pmu_analysis.h +++ b/pmu/pmu_analysis.h @@ -8,7 +8,7 @@ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR * PURPOSE. * See the Mulan PSL v2 for more details. - * Author: Mr.Zhang + * Author: Mr.Lei * Create: 2025-01-17 * Description: definition of singleton class PmuAnalysis for analyze performance data in the KUNPENG_PMU namespace ******************************************************************************/ diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp index 7c5ca25..a8b5cbd 100644 --- a/pmu/pmu_trace_analysis.cpp +++ b/pmu/pmu_trace_analysis.cpp @@ -8,7 +8,7 @@ * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR * PURPOSE. * See the Mulan PSL v2 for more details. - * Author: Mr.Zhang + * Author: Mr.Lei * Create: 2025-01-17 * Description: Pmu trace event analysis module. * Current capability: Analyze the time consumed by system invoking function @@ -30,28 +30,34 @@ static vector SysCallFuncList; const char** PmuSysCallFuncList(unsigned *numFuncs) { lock_guard lg(SysCallListMtx); - if (!SysCallFuncList.empty()) { - *numFuncs = SysCallFuncList.size(); - return SysCallFuncList.data(); - } - enum PmuEventType eventType = TRACE_EVENT; - unsigned int numTraceEvents = 0; - const char **traceEventList = PmuEventList(eventType, &numTraceEvents); - if (traceEventList == nullptr) { - return nullptr; - } - for (int i = 0; i < numTraceEvents; ++i) { - if (const char *pos = strstr(traceEventList[i], SYSCALL_FUNC_ENTER_PREFIX)) { - size_t funcNameLen = strlen(traceEventList[i]) - strlen(SYSCALL_FUNC_ENTER_PREFIX) + 1; - char *syscallFunName = new char[funcNameLen]; - if (syscallFunName == nullptr) { - return nullptr; + SetWarn(SUCCESS); + try { + if (!SysCallFuncList.empty()) { + *numFuncs = SysCallFuncList.size(); + return SysCallFuncList.data(); + } + enum PmuEventType eventType = TRACE_EVENT; + unsigned int numTraceEvents = 0; + const char **traceEventList = PmuEventList(eventType, &numTraceEvents); + if (traceEventList == nullptr) { + return nullptr; + } + for (int i = 0; i < numTraceEvents; ++i) { + if (const char *pos = strstr(traceEventList[i], SYSCALL_FUNC_ENTER_PREFIX)) { + size_t funcNameLen = strlen(traceEventList[i]) - strlen(SYSCALL_FUNC_ENTER_PREFIX) + 1; + char *syscallFunName = new char[funcNameLen]; + if (syscallFunName == nullptr) { + return nullptr; + } + strcpy(syscallFunName, pos + strlen(SYSCALL_FUNC_ENTER_PREFIX)); + SysCallFuncList.emplace_back(syscallFunName); } - strcpy(syscallFunName, pos + strlen(SYSCALL_FUNC_ENTER_PREFIX)); - SysCallFuncList.emplace_back(syscallFunName); } + } catch (...) { + New(LIBPERF_ERR_QUERY_SYSCALL_LIST_FAILED, "Query system call function list failed!"); + return nullptr; } - + New(SUCCESS); *numFuncs = SysCallFuncList.size(); return SysCallFuncList.data(); } @@ -65,9 +71,10 @@ void PmuSysCallFuncListFree() } } SysCallFuncList.clear(); + New(SUCCESS); } -static bool ValidateSysCallName(const char **funList, unsigned numFuns) +static int CheckSysCallName(const char **funList, unsigned numFuns) { const char **sysCallFuns = nullptr; unsigned numSysCall = 0; @@ -86,10 +93,11 @@ static bool ValidateSysCallName(const char **funList, unsigned numFuns) } } if (!isValid) { - return false; + New(LIBPERF_ERR_INVALID_SYSCALL_FUN, "the system call function name error!"); + return LIBPERF_ERR_INVALID_SYSCALL_FUN; } } - return true; + return SUCCESS; } static int CheckTraceAttr(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr) @@ -102,10 +110,9 @@ static int CheckTraceAttr(enum PmuTraceType traceType, struct PmuTraceAttr *trac traceAttr->funcs = PmuSysCallFuncList(&traceAttr->numFuncs); return SUCCESS; } - - if (!ValidateSysCallName(traceAttr->funcs, traceAttr->numFuncs)) { - New(LIBPERF_ERR_INVALID_SYSCALL_FUN, "the system call function name error!"); - return LIBPERF_ERR_INVALID_SYSCALL_FUN; + auto err = CheckSysCallName(traceAttr->funcs, traceAttr->numFuncs); + if (err != SUCCESS) { + return err; } return SUCCESS; @@ -113,22 +120,27 @@ static int CheckTraceAttr(enum PmuTraceType traceType, struct PmuTraceAttr *trac static char **GenerateSysCallFuncTraceEvtList(const char **sysCallFuncs, const unsigned numFuncs, unsigned int &numEvt) { - numEvt = 0; - char **syscallEvts = new char* [numFuncs * 2]; - for (size_t i = 0; i < numFuncs; ++i) { - size_t enterLen = strlen(SYSCALL_FUNC_ENTER_PREFIX) + strlen(sysCallFuncs[i]) + 1; - syscallEvts[numEvt] = new char[enterLen]; - strcpy(syscallEvts[numEvt], SYSCALL_FUNC_ENTER_PREFIX); - strcat(syscallEvts[numEvt], sysCallFuncs[i]); - numEvt++; - - size_t exitLen = strlen(SYSCALL_FUNC_EXIT_PREFIX) + strlen(sysCallFuncs[i]) + 1; - syscallEvts[numEvt] = new char[exitLen]; - strcpy(syscallEvts[numEvt], SYSCALL_FUNC_EXIT_PREFIX); - strcat(syscallEvts[numEvt], sysCallFuncs[i]); - numEvt++; + SetWarn(SUCCESS); + try { + numEvt = 0; + char **syscallEvts = new char* [numFuncs * 2]; + for (size_t i = 0; i < numFuncs; ++i) { + size_t enterLen = strlen(SYSCALL_FUNC_ENTER_PREFIX) + strlen(sysCallFuncs[i]) + 1; + syscallEvts[numEvt] = new char[enterLen]; + strcpy(syscallEvts[numEvt], SYSCALL_FUNC_ENTER_PREFIX); + strcat(syscallEvts[numEvt], sysCallFuncs[i]); + numEvt++; + + size_t exitLen = strlen(SYSCALL_FUNC_EXIT_PREFIX) + strlen(sysCallFuncs[i]) + 1; + syscallEvts[numEvt] = new char[exitLen]; + strcpy(syscallEvts[numEvt], SYSCALL_FUNC_EXIT_PREFIX); + strcat(syscallEvts[numEvt], sysCallFuncs[i]); + numEvt++; + } + } catch (std::bad_alloc&) { + New(COMMON_ERR_NOMEM); } - + New(SUCCESS); return syscallEvts; } @@ -192,8 +204,8 @@ int PmuTraceRead(int pd, struct PmuTraceData **pmuTraceData) *pmuTraceData = nullptr; return 0; } - New(SUCCESS); auto& traceData = KUNPENG_PMU::PmuAnalysis::GetInstance()->AnalyzeTraceData(pd, pmuData, len); + New(SUCCESS); if (!traceData.empty()) { PmuDataFree(pmuData); // free other useless trace data. *pmuTraceData = traceData.data(); diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py index ee35974..f37a384 100644 --- a/python/modules/kperf/perror.py +++ b/python/modules/kperf/perror.py @@ -80,6 +80,7 @@ class Error: LIBPERF_ERR_INVALID_GROUP_ALL_UNCORE = 1037 LIBPERF_ERR_INVALID_TRACE_TYPE = 1038 LIBPERF_ERR_INVALID_SYSCALL_FUN = 1039 + LIBPERF_ERR_QUERY_SYSCALL_LIST_FAILED = 1040 UNKNOWN_ERROR = 9999 -- Gitee From 70a53da0f8b7c4b105c61da1124f1888d5683fbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Sun, 19 Jan 2025 09:02:25 +0800 Subject: [PATCH 08/10] add some collect trace data ut files --- test/test_perf/test_trace_analysis.cpp | 93 ++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100644 test/test_perf/test_trace_analysis.cpp diff --git a/test/test_perf/test_trace_analysis.cpp b/test/test_perf/test_trace_analysis.cpp new file mode 100644 index 0000000..434af77 --- /dev/null +++ b/test/test_perf/test_trace_analysis.cpp @@ -0,0 +1,93 @@ +/****************************************************************************** + * 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: Mr.Lei + * Create: 2025-01-18 + * Description: test for analyszing the trace data + ******************************************************************************/ +#include "test_common.h" + +using namespace std; + +class TestAnaylzeData : public testing::Test { +public: + void TearDown() { + if (appPid != 0) { + KillApp(appPid); + appPid = 0; + } + if (pmuTraceData != nullptr) { + PmuTraceDataFree(pmuData); + pmuTraceData = nullptr; + } + PmuTraceClose(pd); + } + +protected: + PmuTraceAttr GetPmuTraceAttr(char **funcs, unsigned numFuncs) { + PmuTraceAttr attr = {0}; + attr.funcs = funcs; + attr.numFuncs = numFuncs; + int pidList[1]; + attr.pidList = pidList; + attr.numPid = 0; + attr.cpuList = nullptr; + attr.numCpu = 0; + return attr; + } + + void EnableTracePointer(unsigned int second) { + PmuTraceEnable(pd); + sleep(second); + PmuTraceDisable(pd); + } + + int pd; + pid_t appPid = 0; + PmuTraceData *pmuTraceData = nullptr; +}; + +/** + * @brief test for configing param error + */ +TEST_F(TestAnaylzeData, config_param_error) { + auto traceAttr = GetPmuTraceAttr("epoll_pwait", 1); + int pd = PmuTraceOpen(SAMPLING, &traceAttr); + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_TRACE_TYPE); + auto traceAttr2 = GetPmuTraceAttr("shced_yield", 1); + pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr2); + ASSERT_EQ(pd, -1); + ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_SYSCALL_FUN); +} + +/** + * @brief test for collecting single syscall trace data + */ +TEST_F(TestAnaylzeData, collect_single_trace_data_success) { + auto traceAttr = GetPmuTraceAttr("epoll_pwait", 1); + int pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); + ASSERT_NE(pd, -1); + EnableTracePointer(1); + int len = PmuTraceRead(pd, &pmuTraceData); + EXPECT_TRUE(pmuTraceData != nullptr); +} + +/** + * @brief test for collecting double syscall trace data + */ +TEST_F(TestAnaylzeData, collect_double_trace_data_success) { + auto traceAttr = GetPmuTraceAttr(["read", "write"], 2); + int pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); + ASSERT_NE(pd, -1); + EnableTracePointer(1); + int len = PmuTraceRead(pd, &pmuTraceData); + EXPECT_TRUE(pmuTraceData != nullptr); +} \ No newline at end of file -- Gitee From b45dfd55339caa20d405d9104d0fd06fc650e054 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Mon, 20 Jan 2025 18:35:50 +0800 Subject: [PATCH 09/10] fix some error add some ut files --- pmu/pmu_trace_analysis.cpp | 8 +- .../case/test_syscall_read_write.cpp | 46 ++++++++++ test/test_perf/case/test_syscall_sleep.cpp | 9 ++ test/test_perf/test_trace_analysis.cpp | 92 ++++++++++++------- 4 files changed, 121 insertions(+), 34 deletions(-) create mode 100644 test/test_perf/case/test_syscall_read_write.cpp create mode 100644 test/test_perf/case/test_syscall_sleep.cpp diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp index a8b5cbd..f47ecf7 100644 --- a/pmu/pmu_trace_analysis.cpp +++ b/pmu/pmu_trace_analysis.cpp @@ -137,11 +137,15 @@ static char **GenerateSysCallFuncTraceEvtList(const char **sysCallFuncs, const u strcat(syscallEvts[numEvt], sysCallFuncs[i]); numEvt++; } + New(SUCCESS); + return syscallEvts; } catch (std::bad_alloc&) { New(COMMON_ERR_NOMEM); + return nullptr; + } catch (std::exception& ex) { + New(COMMON_ERR_UNKNOWN, ex.what()); + return nullptr; } - New(SUCCESS); - return syscallEvts; } static void EraseTraceAttrEvtList(char **evtList, unsigned numEvt) diff --git a/test/test_perf/case/test_syscall_read_write.cpp b/test/test_perf/case/test_syscall_read_write.cpp new file mode 100644 index 0000000..d6d17bf --- /dev/null +++ b/test/test_perf/case/test_syscall_read_write.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +int main() { + int pipefd[2]; + char buffer; + + if (pipe(pipefd) == -1) { + perror("pipe"); + return 1; + } + + const char initial_data = 'A'; + if (write(pipefd[1], &initial_data, 1) != 1) { + perror("initial write"); + close(pipefd[0]); + close(pipefd[1]); + return 1; + } + + while (true) { + ssize_t bytesRead = read(pipefd[0], &buffer, 1); + + if (bytesRead == -1) { + perror("read"); + break; + } else if (bytesRead == 0) { + std::cout << "End of pipe reached." << std::endl; + break; + } + + ssize_t bytesWritten = write(pipefd[1], &buffer, 1); + + if (bytesWritten == -1) { + perror("write"); + break; + } + + } + + close(pipefd[0]); + close(pipefd[1]); + + return 0; +} \ No newline at end of file diff --git a/test/test_perf/case/test_syscall_sleep.cpp b/test/test_perf/case/test_syscall_sleep.cpp new file mode 100644 index 0000000..e564ac0 --- /dev/null +++ b/test/test_perf/case/test_syscall_sleep.cpp @@ -0,0 +1,9 @@ +#include + +int main() { + while (true) { + sleep(0.5); + } + + return 0; +} \ No newline at end of file diff --git a/test/test_perf/test_trace_analysis.cpp b/test/test_perf/test_trace_analysis.cpp index 434af77..d4ed714 100644 --- a/test/test_perf/test_trace_analysis.cpp +++ b/test/test_perf/test_trace_analysis.cpp @@ -23,27 +23,15 @@ public: KillApp(appPid); appPid = 0; } - if (pmuTraceData != nullptr) { - PmuTraceDataFree(pmuData); - pmuTraceData = nullptr; + if (data != nullptr) { + PmuTraceDataFree(data); + data = nullptr; } PmuTraceClose(pd); } protected: - PmuTraceAttr GetPmuTraceAttr(char **funcs, unsigned numFuncs) { - PmuTraceAttr attr = {0}; - attr.funcs = funcs; - attr.numFuncs = numFuncs; - int pidList[1]; - attr.pidList = pidList; - attr.numPid = 0; - attr.cpuList = nullptr; - attr.numCpu = 0; - return attr; - } - - void EnableTracePointer(unsigned int second) { + void EnableTracePointer(unsigned pd, unsigned int second) { PmuTraceEnable(pd); sleep(second); PmuTraceDisable(pd); @@ -51,18 +39,18 @@ protected: int pd; pid_t appPid = 0; - PmuTraceData *pmuTraceData = nullptr; + PmuTraceData *data = nullptr; }; /** * @brief test for configing param error */ TEST_F(TestAnaylzeData, config_param_error) { - auto traceAttr = GetPmuTraceAttr("epoll_pwait", 1); - int pd = PmuTraceOpen(SAMPLING, &traceAttr); - ASSERT_EQ(pd, -1); - ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_TRACE_TYPE); - auto traceAttr2 = GetPmuTraceAttr("shced_yield", 1); + const char *func1 = "testName"; + const char *funcs[1] = {func1}; + PmuTraceAttr traceAttr = {0}; + traceAttr.funcs = funcs; + traceAttr.numFuncs = 1; pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr2); ASSERT_EQ(pd, -1); ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_SYSCALL_FUN); @@ -72,22 +60,62 @@ TEST_F(TestAnaylzeData, config_param_error) { * @brief test for collecting single syscall trace data */ TEST_F(TestAnaylzeData, collect_single_trace_data_success) { - auto traceAttr = GetPmuTraceAttr("epoll_pwait", 1); - int pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); + appPid = RunTestApp("test_12threads"); + int pidList[1] = {appPid}; + const char *func1 = "futex"; + const char *funcs[1] = {func1}; + PmuTraceAttr traceAttr = {0}; + traceAttr.funcs = funcs; + traceAttr.numFuncs = 1; + traceAttr.pidList = pidList; + traceAttr.numPid = 1; + + pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); + ASSERT_NE(pd, -1); + EnableTracePointer(pd, 1); + int len = PmuTraceRead(pd, &data); + EXPECT_TRUE(data != nullptr); +} + +/** + * @brief test for collecting single syscall trace data + */ +TEST_F(TestAnaylzeData, collect_sleep_trace_data_success) { + appPid = RunTestApp("test_syscall_sleep"); + int pidList[1] = {appPid}; + const char *func1 = "clock_nanosleep"; + const char *funcs[1] = {func1}; + PmuTraceAttr traceAttr = {0}; + traceAttr.funcs = funcs; + traceAttr.numFuncs = 1; + traceAttr.pidList = pidList; + traceAttr.numPid = 1; + + pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); ASSERT_NE(pd, -1); - EnableTracePointer(1); - int len = PmuTraceRead(pd, &pmuTraceData); - EXPECT_TRUE(pmuTraceData != nullptr); + EnableTracePointer(pd, 1); + int len = PmuTraceRead(pd, &data); + EXPECT_TRUE(data != nullptr); } /** * @brief test for collecting double syscall trace data */ TEST_F(TestAnaylzeData, collect_double_trace_data_success) { - auto traceAttr = GetPmuTraceAttr(["read", "write"], 2); - int pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); + appPid = RunTestApp("test_syscall_read_write"); + int pidList[1] = {appPid}; + const char *func1 = "write"; + const char *func2 = "read"; + const char *funcs[2] = {func1, func2}; + PmuTraceAttr traceAttr = {0}; + traceAttr.funcs = funcs; + traceAttr.numFuncs = 2; + traceAttr.pidList = pidList; + traceAttr.numPid = 1; + + pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); ASSERT_NE(pd, -1); - EnableTracePointer(1); - int len = PmuTraceRead(pd, &pmuTraceData); - EXPECT_TRUE(pmuTraceData != nullptr); + EnableTracePointer(pd, 1); + int len = PmuTraceRead(pd, &data); + EXPECT_TRUE(data != nullptr); } \ No newline at end of file -- Gitee From 7661b72e94a8279c9501a30634d8c4a518980184 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=96=E9=BE=99?= Date: Mon, 20 Jan 2025 18:49:16 +0800 Subject: [PATCH 10/10] fix some spell error --- pmu/pmu_trace_analysis.cpp | 2 +- test/test_perf/test_trace_analysis.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pmu/pmu_trace_analysis.cpp b/pmu/pmu_trace_analysis.cpp index f47ecf7..9a3cb2b 100644 --- a/pmu/pmu_trace_analysis.cpp +++ b/pmu/pmu_trace_analysis.cpp @@ -143,7 +143,7 @@ static char **GenerateSysCallFuncTraceEvtList(const char **sysCallFuncs, const u New(COMMON_ERR_NOMEM); return nullptr; } catch (std::exception& ex) { - New(COMMON_ERR_UNKNOWN, ex.what()); + New(UNKNOWN_ERROR, ex.what()); return nullptr; } } diff --git a/test/test_perf/test_trace_analysis.cpp b/test/test_perf/test_trace_analysis.cpp index d4ed714..f613646 100644 --- a/test/test_perf/test_trace_analysis.cpp +++ b/test/test_perf/test_trace_analysis.cpp @@ -51,7 +51,7 @@ TEST_F(TestAnaylzeData, config_param_error) { PmuTraceAttr traceAttr = {0}; traceAttr.funcs = funcs; traceAttr.numFuncs = 1; - pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr2); + pd = PmuTraceOpen(TRACE_SYS_CALL, &traceAttr); ASSERT_EQ(pd, -1); ASSERT_EQ(Perrorno(), LIBPERF_ERR_INVALID_SYSCALL_FUN); } -- Gitee