* 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: Pmu trace event analysis module.
* Current capability: Analyze the time consumed by system invoking function
******************************************************************************/
#include <iostream>
#include <vector>
#include <cstring>
#include "pmu_list.h"
#include "pmu_analysis.h"
#include "pcerr.h"
#include "pmu.h"
using namespace pcerr;
using namespace KUNPENG_PMU;
using namespace std;
static std::mutex SysCallListMtx;
static vector<const char*> SysCallFuncList;
const char** PmuSysCallFuncList(unsigned *numFuncs)
{
#ifdef IS_X86
New(LIBPERF_ERR_INTERFACE_NOT_SUPPORT_X86);
*numFuncs = 0;
return nullptr;
#else
lock_guard<mutex> lg(SysCallListMtx);
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];
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();
#endif
}
void PmuSysCallFuncListFree()
{
lock_guard<mutex> lg(SysCallListMtx);
for (auto &funcName : SysCallFuncList) {
if (funcName != nullptr) {
delete[] funcName;
}
}
SysCallFuncList.clear();
New(SUCCESS);
}
static int CheckSysCallName(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) {
New(LIBPERF_ERR_INVALID_SYSCALL_FUN, "the system call function name error!");
return LIBPERF_ERR_INVALID_SYSCALL_FUN;
}
}
return SUCCESS;
}
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) {
return PmuAnalysis::GetInstance()->GenerateSysCallTable();
}
auto err = CheckSysCallName(traceAttr->funcs, traceAttr->numFuncs);
if (err != SUCCESS) {
return err;
}
return SUCCESS;
}
static void EraseTraceAttrEvtList(char **evtList, unsigned numEvt)
{
for (size_t i = 0; i < numEvt; ++i) {
delete[] evtList[i];
}
delete[] evtList;
}
static bool PdValid(const int pd)
{
return PmuAnalysis::GetInstance()->IsPdAlive(pd);
}
static char **GeneratePmuAttrEvtList(const char **sysCallFuncs, const unsigned numFuncs, unsigned int &numEvt)
{
SetWarn(SUCCESS);
try {
if (sysCallFuncs == nullptr) {
numEvt = 2;
char **syscallEvts = new char* [numEvt];
size_t exitLen = strlen(EXIT_RAW_SYSCALL) + 1;
syscallEvts[0] = new char[exitLen];
strcpy(syscallEvts[0], EXIT_RAW_SYSCALL);
size_t enterLen = strlen(ENTER_RAW_SYSCALL) + 1;
syscallEvts[1] = new char[enterLen];
strcpy(syscallEvts[1], ENTER_RAW_SYSCALL);
New(SUCCESS);
return syscallEvts;
} else {
numEvt = 0;
char **syscallEvts = new char* [numFuncs * 2];
for (size_t i = 0; i < numFuncs; ++i) {
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++;
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++;
}
New(SUCCESS);
return syscallEvts;
}
} catch (std::bad_alloc&) {
New(COMMON_ERR_NOMEM);
return nullptr;
} catch (std::exception& ex) {
New(UNKNOWN_ERROR, ex.what());
return nullptr;
}
}
int PmuTraceOpen(enum PmuTraceType traceType, struct PmuTraceAttr *traceAttr)
{
#ifdef IS_X86
New(LIBPERF_ERR_INTERFACE_NOT_SUPPORT_X86);
return -1;
#else
SetWarn(SUCCESS);
if (traceAttr == nullptr) {
New(LIBPERF_ERR_NULL_POINTER, "PmuTraceAttr cannot be null");
return -1;
}
auto err = CheckTraceAttr(traceType, traceAttr);
if (err != SUCCESS) {
return -1;
}
PmuAttr attr = {0};
attr.evtList = GeneratePmuAttrEvtList(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;
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;
}
return pd;
#endif
}
int PmuTraceEnable(int pd)
{
return PmuEnable(pd);
}
int PmuTraceDisable(int pd)
{
return PmuDisable(pd);
}
int PmuTraceRead(int pd, struct PmuTraceData **pmuTraceData)
{
#ifdef IS_X86
New(LIBPERF_ERR_INTERFACE_NOT_SUPPORT_X86);
return -1;
#else
PmuData *pmuData = nullptr;
unsigned len = PmuRead(pd, &pmuData);
if (len == -1) {
return -1;
}
if (len == 0) {
*pmuTraceData = nullptr;
return 0;
}
try {
if (!PdValid(pd)) {
New(LIBPERF_ERR_INVALID_PD);
return -1;
}
New(SUCCESS);
bool isAllCollect =
(strcmp(pmuData[0].evt, ENTER_RAW_SYSCALL) == 0) || (strcmp(pmuData[0].evt, EXIT_RAW_SYSCALL) == 0);
std::vector<PmuTraceData>& traceData = isAllCollect ? PmuAnalysis::GetInstance()->AnalyzeRawTraceData(pd, pmuData, len)
: PmuAnalysis::GetInstance()->AnalyzeTraceData(pd, pmuData, len);
if (!traceData.empty()) {
*pmuTraceData = traceData.data();
return traceData.size();
} else {
*pmuTraceData = nullptr;
return 0;
}
} catch (std::bad_alloc&) {
New(COMMON_ERR_NOMEM);
return -1;
} catch (std::exception& ex) {
New(UNKNOWN_ERROR, ex.what());
return -1;
}
#endif
}
void PmuTraceClose(int pd)
{
SetWarn(SUCCESS);
try {
KUNPENG_PMU::PmuAnalysis::GetInstance()->Close(pd);
PmuSysCallFuncListFree();
New(SUCCESS);
} catch (std::bad_alloc&) {
New(COMMON_ERR_NOMEM);
} catch (std::exception& ex) {
New(UNKNOWN_ERROR, ex.what());
}
}
void PmuTraceDataFree(struct PmuTraceData *pmuTraceData)
{
SetWarn(SUCCESS);
KUNPENG_PMU::PmuAnalysis::GetInstance()->FreeTraceData(pmuTraceData);
New(SUCCESS);
}