* Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.
* libkperf licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
* Author: Mr.Ye
* Create: 2024-04-03
* Description: definitions of interfaces of querying and freeing pmu event list
******************************************************************************/
#include <iostream>
#include <cstring>
#include <dirent.h>
#include <functional>
#include <unordered_map>
#include <vector>
#include <mutex>
#include <fstream>
#include "core.h"
#include "evt.h"
#include "pcerr.h"
#include "pmu.h"
#include "common.h"
using namespace pcerr;
using namespace std;
using EvtQueryer = function<const char**(unsigned*, bool eventCheck)>;
static const string SLASH = "/";
static const string COLON = ":";
static const string EVENT_DIR = "/events/";
static std::mutex pmuEventListMtx;
#ifdef IS_X86
static vector<const char*> supportDevPrefixs = {"uncore_iio", "uncore_imc", "cpu", "amd_iommu", "power", "msr"};
#else
static vector<const char*> supportDevPrefixs = {"hisi", "smmuv3", "hns3", "armv8"};
#endif
static vector<const char*> uncoreEventList;
static vector<const char*> traceEventList;
static vector<const char*> coreEventList;
static vector<const char*> checkCoreEventList;
static void GetEventName(const string& devName, vector<const char*>& eventList)
{
DIR* dir;
struct dirent* entry;
auto path = SYS_DEVICE_PATH + devName + EVENT_DIR;
dir = opendir(path.c_str());
if (dir == nullptr) {
return;
}
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type != DT_REG) {
continue;
}
string fileName(entry->d_name);
#ifdef IS_X86
if (fileName.find('.') != string::npos) {
continue;
}
#endif
auto eventName = devName;
eventName += SLASH + fileName;
eventName += SLASH;
char* eventNameCopy = new char[eventName.length() + 1];
strcpy(eventNameCopy, eventName.c_str());
eventList.emplace_back(eventNameCopy);
}
closedir(dir);
}
static void GetTraceSubFolder(const std::string& traceFolder, const string& devName, vector<const char*>& eventList)
{
struct dirent* entry;
auto path = traceFolder + devName;
DIR *dir = opendir(path.c_str());
if (dir == nullptr) {
return;
}
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type != DT_DIR) {
continue;
}
string folderName(entry->d_name);
if (folderName.find('.') == string::npos) {
auto eventName = devName;
eventName += COLON + folderName;
char* eventNameCopy = new char[eventName.length() + 1];
strcpy(eventNameCopy, eventName.c_str());
eventList.emplace_back(eventNameCopy);
}
}
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 && errno == ENOENT) {
return false;
}
close(fd);
return true;
}
const char **GetCoreEventList(unsigned *numEvt, bool eventCheck)
{
if (eventCheck) {
*numEvt = checkCoreEventList.size();
return checkCoreEventList.data();
}
*numEvt = coreEventList.size();
return coreEventList.data();
}
const char** QueryCoreEvent(unsigned *numEvt, bool eventCheck)
{
if (!coreEventList.empty() && !eventCheck) {
*numEvt = coreEventList.size();
return coreEventList.data();
}
if (!checkCoreEventList.empty() && eventCheck) {
*numEvt = checkCoreEventList.size();
return checkCoreEventList.data();
}
auto coreEventMap = KUNPENG_PMU::CORE_EVENT_MAP.at(GetCpuType());
for (auto& pair : coreEventMap) {
auto eventName = pair.first;
char* eventNameCopy = new char[eventName.length() + 1];
strcpy(eventNameCopy, eventName.c_str());
if (eventCheck && PerfEventSupported(pair.second.type, pair.second.config)) {
checkCoreEventList.emplace_back(eventNameCopy);
} else {
coreEventList.emplace_back(eventNameCopy);
}
}
DIR* dir;
struct dirent* entry;
auto pmuDevPath = GetPmuDevicePath();
if (pmuDevPath.empty()) {
return GetCoreEventList(numEvt, eventCheck);
}
string path = pmuDevPath + "/events/";
dir = opendir(path.c_str());
if (dir == nullptr) {
return GetCoreEventList(numEvt, eventCheck);
}
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type == DT_REG) {
string evtName = entry->d_name;
char* eventNameCopy = new char[evtName.length() + 1];
strcpy(eventNameCopy, evtName.c_str());
if (eventCheck) {
checkCoreEventList.emplace_back(eventNameCopy);
} else {
coreEventList.emplace_back(eventNameCopy);
}
}
}
closedir(dir);
return GetCoreEventList(numEvt, eventCheck);
}
const char** QueryUncoreEvent(unsigned *numEvt, bool eventCheck)
{
if (!uncoreEventList.empty()) {
*numEvt = uncoreEventList.size();
return uncoreEventList.data();
}
DIR* dir;
struct dirent* entry;
dir = opendir(SYS_DEVICE_PATH.c_str());
if (dir == nullptr) {
return nullptr;
}
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type != DT_DIR && entry->d_type != DT_LNK) {
continue;
}
string folderName = entry->d_name;
for (auto devPrefix: supportDevPrefixs) {
if (folderName.find(devPrefix) == 0) {
GetEventName(folderName, uncoreEventList);
break;
}
}
}
closedir(dir);
*numEvt = uncoreEventList.size();
return uncoreEventList.data();
}
const char** QueryTraceEvent(unsigned *numEvt, bool eventCheck)
{
if (!traceEventList.empty()) {
*numEvt = traceEventList.size();
return traceEventList.data();
}
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());
if (dir == nullptr) {
return nullptr;
}
while ((entry = readdir(dir)) != nullptr) {
if (entry->d_type == DT_DIR) {
string folderName = entry->d_name;
if (folderName.find('.') == string::npos) {
string devName(entry->d_name);
GetTraceSubFolder(traceFolder, devName, traceEventList);
}
}
}
closedir(dir);
*numEvt = traceEventList.size();
return traceEventList.data();
}
const char** QueryAllEvent(unsigned *numEvt, bool eventCheck) {
unsigned coreNum, uncoreNum, traceNum;
const char** coreList = QueryCoreEvent(&coreNum, eventCheck);
const char** uncoreList = QueryUncoreEvent(&uncoreNum, eventCheck);
const char** traceList = QueryTraceEvent(&traceNum, eventCheck);
unsigned totalSize = 0;
if (coreList != nullptr) {
totalSize += coreNum;
}
if (uncoreList != nullptr) {
totalSize += uncoreNum;
}
if (traceList != nullptr) {
totalSize += traceNum;
}
const char** combinedList = new const char*[totalSize];
unsigned index = 0;
if (coreList != nullptr) {
memcpy(combinedList, coreList, coreNum * sizeof(const char*));
index += coreNum;
}
if (uncoreList != nullptr) {
memcpy(combinedList + index, uncoreList, uncoreNum * sizeof(const char*));
index += uncoreNum;
}
if (traceList != nullptr) {
memcpy(combinedList + index, traceList, traceNum * sizeof(const char*));
}
*numEvt = totalSize;
return combinedList;
}
static const unordered_map<int, EvtQueryer> QueryMap{
{PmuEventType::CORE_EVENT, QueryCoreEvent},
{PmuEventType::UNCORE_EVENT, QueryUncoreEvent},
{PmuEventType::TRACE_EVENT, QueryTraceEvent},
{PmuEventType::ALL_EVENT, QueryAllEvent},
};
const char** PmuEventList(enum PmuEventType eventType, unsigned *numEvt, bool eventNeedCheck)
{
lock_guard<mutex> lg(pmuEventListMtx);
const char** eventList;
if (QueryMap.find(eventType) == QueryMap.end()) {
New(LIBPERF_ERR_QUERY_EVENT_TYPE_INVALID, "Event type is invalid.");
return nullptr;
}
try {
eventList = QueryMap.at(eventType)(numEvt, eventNeedCheck);
} catch (...) {
New(LIBPERF_ERR_QUERY_EVENT_LIST_FAILED, "Query event failed.");
return nullptr;
}
return eventList;
}
const char** PmuEventList(enum PmuEventType eventType, unsigned *numEvt)
{
return PmuEventList(eventType, numEvt, true);
}
static void PmuEventListFreeSingle(vector<const char*>& eventList)
{
for (auto evt : eventList) {
if (evt != nullptr && evt[0] != '\0')
delete[] evt;
}
eventList.clear();
}
void PmuEventListFree()
{
lock_guard<mutex> lg(pmuEventListMtx);
PmuEventListFreeSingle(coreEventList);
PmuEventListFreeSingle(checkCoreEventList);
PmuEventListFreeSingle(uncoreEventList);
PmuEventListFreeSingle(traceEventList);
New(SUCCESS);
}