* 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: uncore event configuration query
******************************************************************************/
#include <fstream>
#include <sstream>
#include <dirent.h>
#include <string>
#include "common.h"
#include "log.h"
#include "pcerr.h"
#include "pfm_event.h"
#include "pmu_event.h"
#include "uncore.h"
using namespace std;
using namespace KUNPENG_PMU;
static std::unordered_map<std::string, std::unordered_map<string, uint64_t>> unCoreRawFieldsValues;
static int GetDeviceType(const string &devName)
{
string typePath = SYS_DEVICE_PATH + devName + "/type";
std::string realPath = GetRealPath(typePath);
if (!IsValidPath(realPath)) {
return -1;
}
ifstream typeIn(realPath);
if (!typeIn.is_open()) {
return -1;
}
string typeStr;
typeIn >> typeStr;
return stoi(typeStr);
}
static std::vector<int> GetCpuMask(const string &devName)
{
std::vector<int> maskList;
string maskPath = SYS_DEVICE_PATH + devName + "/cpumask";
std::string realPath = GetRealPath(maskPath);
if (!IsValidPath(realPath)) {
return maskList;
}
ifstream maskIn(realPath);
if (!maskIn.is_open()) {
return maskList;
}
char maskStr[1024];
maskIn >> maskStr;
if (maskStr[0] == '-') {
return maskList;
}
char *tokStr = strtok(maskStr, ",");
while (tokStr != nullptr) {
if (strstr(tokStr, "-") != nullptr) {
int minCpu, maxCpu;
if (sscanf(tokStr, "%d-%d", &minCpu, &maxCpu) != 2) {
continue;
}
for (int i = minCpu; i <= maxCpu; i++) {
maskList.push_back(i);
}
} else {
int aloneNumber;
if (sscanf(tokStr, "%d", &aloneNumber) == 1) {
maskList.push_back(aloneNumber);
}
}
tokStr = strtok(nullptr, ",");
}
return maskList;
}
static int64_t TransferStrToHex(const std::string& str) {
int64_t intData;
std::istringstream iss(str);
iss >> std::hex >> intData;
return intData;
}
static int64_t GetUncoreEventConfig(const char* pmuName)
{
int64_t config;
string strName(pmuName);
auto findSlash = strName.find('/');
string devName = strName.substr(0, findSlash);
string evtName = strName.substr(devName.size() + 1, strName.size() - 1 - (devName.size() + 1));
string evtPath = SYS_DEVICE_PATH + devName + "/events/" + evtName;
std::string realPath = GetRealPath(evtPath);
if (!IsValidPath(realPath)) {
return -1;
}
ifstream evtIn(realPath);
if (!evtIn.is_open()) {
return -1;
}
string configStr;
evtIn >> configStr;
auto findEq = configStr.find('=');
if (findEq == string::npos) {
return -1;
}
#ifdef IS_X86
auto umaskEq = configStr.find("umask");
if (umaskEq != string::npos) {
auto CommaEq = configStr.find(",");
if (CommaEq == string::npos) {
return -1;
}
auto lowStr = configStr.substr(findEq + 1, CommaEq - findEq);
int64_t low = TransferStrToHex(lowStr);
auto highStr = configStr.substr(umaskEq + 6, configStr.size() - umaskEq - 6);
int64_t high = TransferStrToHex(highStr);
config = (high << 8) + low;
return config;
}
#endif
auto subStr = configStr.substr(findEq + 1, configStr.size() - findEq);
std::istringstream iss(subStr);
iss >> std::hex >> config;
return config;
}
int FillUncoreFields(const char* pmuName, PmuEvt *evt)
{
string strName(pmuName);
auto findSlash = strName.find('/');
string devName = strName.substr(0, findSlash);
string evtName = strName.substr(devName.size() + 1, strName.size() - 1 - (devName.size() + 1));
int devType = GetDeviceType(devName);
if (devType == -1) {
return UNKNOWN_ERROR;
}
evt->type = devType;
std::vector<int> cpuMaskList = GetCpuMask(devName);
evt->cpuMaskList = cpuMaskList;
evt->name = pmuName;
return SUCCESS;
}
static std::unordered_map<std::string, UncoreConfigBitFiled> ReadConfigFormatFiles(const string &devName)
{
string formatPath = SYS_DEVICE_PATH + devName + "/format";
std::string realPath = GetRealPath(formatPath);
if (!IsValidPath(realPath)) {
return {};
}
DIR* dir = opendir(realPath.c_str());
if (!dir) {
DBG_PRINT("Error: Unable to open directrory: %s\n.", realPath);
return {};
}
struct dirent* entry;
unordered_map<string, UncoreConfigBitFiled> supportConfigParams;
while ((entry = readdir(dir)) != nullptr) {
string fileName = entry->d_name;
if (fileName == "." || fileName == "..") {
continue;
}
ifstream ifs(realPath + "/" + fileName);
if (!ifs.is_open()) {
continue;
}
string line;
getline(ifs, line);
if (line.empty()) {
continue;
}
auto colonPos = line.find(':');
if (colonPos == string::npos) {
continue;
}
string paramName = fileName;
string fieldName = line.substr(0, colonPos);
string bitFiledInfo = line.substr(colonPos + 1);
unsigned startBit = 0;
unsigned endBit = 0;
auto hyphenPos = bitFiledInfo.find('-');
if (hyphenPos == string::npos) {
startBit = stoi(bitFiledInfo);
endBit = startBit;
} else {
startBit = stoi(bitFiledInfo.substr(0, hyphenPos));
endBit = stoi(bitFiledInfo.substr(hyphenPos + 1));
}
supportConfigParams[paramName] = UncoreConfigBitFiled{fieldName, startBit, endBit};
if (paramName == "event") {
supportConfigParams["config"] = UncoreConfigBitFiled{fieldName, startBit, endBit};
}
}
closedir(dir);
return supportConfigParams;
}
static std::pair<string, string> ReadSupportEvtValue(const string &devName, const string &evtName)
{
string evtPath = SYS_DEVICE_PATH + devName + "/events/" + evtName;
std::string realPath = GetRealPath(evtPath);
if (!IsValidPath(realPath)) {
return {};
}
ifstream ifs(realPath);
if (!ifs.is_open()) {
DBG_PRINT("Error: Unable to open evtpath file: %s\n.", realPath);
return {};
}
std::pair<string, string> evtConfigRegs;
string line;
while (getline(ifs, line)) {
if (line.empty()) {
continue;
}
auto equalPos = line.find('=');
if (equalPos == string::npos) {
continue;
}
string key = line.substr(0, equalPos);
if (key == "config") {
key = "event";
}
string value = line.substr(equalPos + 1);
evtConfigRegs = make_pair(key, value);
}
return evtConfigRegs;
}
static std::unordered_map<string, string> ParseEventConfig(const string &eventStr, const string &devName)
{
std::unordered_map<string, string> parseParams;
for (size_t pos = 0; pos < eventStr.size(); ) {
size_t nextPos = eventStr.find(',', pos);
if (nextPos == string::npos) {
nextPos = eventStr.size();
}
std::string paramStr = eventStr.substr(pos, nextPos - pos);
auto equalPos = paramStr.find('=');
if (equalPos != string::npos) {
string evtName = paramStr.substr(0, equalPos);
string configValue = paramStr.substr(equalPos + 1);
parseParams[evtName] = configValue;
} else {
string evtName = paramStr;
auto evtConfigPair = ReadSupportEvtValue(devName, evtName);
if (evtConfigPair.first.empty() || evtConfigPair.second.empty()) {
std::cerr << "Error: event Name is error" << std::endl;
return {};
}
parseParams[evtConfigPair.first] = evtConfigPair.second;
}
pos = nextPos + 1;
}
return parseParams;
}
static bool ValidConfigValueAndGenerateFields(const std::unordered_map<string, UncoreConfigBitFiled> &supportConfigParams,
const std::unordered_map<string, string> &parseParams, std::unordered_map<string, uint64_t> &fields)
{
for (const auto& pair : parseParams) {
const auto& key = pair.first;
const auto& value = pair.second;
auto findConfig = supportConfigParams.find(key);
if (findConfig == supportConfigParams.end()) {
DBG_PRINT("Error: Invalid config param %s\n", key.c_str());
pcerr::New(LIBPERF_ERR_QUERY_EVENT_LIST_FAILED, "Invalid config param " + key);
return false;
}
auto& configBitFiled = findConfig->second;
unsigned bitWidth = configBitFiled.endBit - configBitFiled.startBit + 1;
uint64_t maxValue = (1ULL << bitWidth) - 1;
uint64_t configValue = 0;
try {
configValue = stoull(value, nullptr, 0);
} catch (const std::invalid_argument& e) {
DBG_PRINT("Error: Invalid config value %s\n", value.c_str());
pcerr::New(LIBPERF_ERR_QUERY_EVENT_LIST_FAILED, "Invalid config value " + value);
return false;
}
if (configValue < 0 || configValue > maxValue) {
DBG_PRINT("Error: config value: %s is too big or negative number.\n", value.c_str());
pcerr::New(LIBPERF_ERR_QUERY_EVENT_LIST_FAILED, "config value: " + value + " is too big or negative number for format, maximum is " + to_string(maxValue));
return false;
}
uint64_t shiftedValue = configValue << configBitFiled.startBit;
fields[configBitFiled.configName] |= shiftedValue;
}
return true;
}
static bool CheckSlashIsValid(const std::string& pmuName)
{
if (pmuName.empty() || pmuName.back() != '/') {
return false;
}
int slashCount = 0;
for (auto c : pmuName) {
if (c == '/') {
slashCount++;
}
}
if (slashCount != 2) {
return false;
}
return true;
}
bool CheckUncoreRawEvent(const char *pmuName)
{
if (!CheckSlashIsValid(pmuName)) {
return false;
}
string strName = pmuName;
auto firstFindSlash = strName.find('/');
auto lastFindSlash = strName.rfind('/');
string devName = strName.substr(0, firstFindSlash);
auto supportConfigParams = ReadConfigFormatFiles(devName);
if (supportConfigParams.empty()) {
return false;
}
std::string eventStr = strName.substr(firstFindSlash + 1, lastFindSlash - 1 - firstFindSlash);
auto parseParams = ParseEventConfig(eventStr, devName);
if (parseParams.empty()) {
return false;
}
std::unordered_map<string, uint64_t> fieldsValues;
if (!ValidConfigValueAndGenerateFields(supportConfigParams, parseParams, fieldsValues)) {
return false;
}
unCoreRawFieldsValues[strName] = fieldsValues;
return true;
}
struct PmuEvt* GetUncoreEvent(const char* pmuName, int collectType)
{
int64_t config = GetUncoreEventConfig(pmuName);
if (config == -1) {
return nullptr;
}
auto* pmuEvtPtr = new PmuEvt {0};
pmuEvtPtr->config = config;
pmuEvtPtr->name = pmuName;
pmuEvtPtr->pmuType = UNCORE_TYPE;
pmuEvtPtr->collectType = collectType;
auto err = FillUncoreFields(pmuName, pmuEvtPtr);
if (err != SUCCESS) {
delete pmuEvtPtr;
return nullptr;
}
if (strstr(pmuName, "armv8_pmuv3_0")) {
pmuEvtPtr->pmuType = CORE_TYPE;
}
return pmuEvtPtr;
}
struct PmuEvt* GetUncoreRawEvent(const char* pmuName, int collectType)
{
if (unCoreRawFieldsValues.empty()) {
return nullptr;
}
auto fieldsValues = unCoreRawFieldsValues[(std::string)pmuName];
auto* pmuEvtPtr = new PmuEvt {0};
pmuEvtPtr->config = fieldsValues.find("config") == fieldsValues.end() ? 0 : fieldsValues.at("config");
pmuEvtPtr->config1 = fieldsValues.find("config1") == fieldsValues.end() ? 0 : fieldsValues.at("config1");
pmuEvtPtr->config2 = fieldsValues.find("config2") == fieldsValues.end() ? 0 : fieldsValues.at("config2");
pmuEvtPtr->name = pmuName;
pmuEvtPtr->pmuType = UNCORE_RAW_TYPE;
pmuEvtPtr->collectType = collectType;
auto err = FillUncoreFields(pmuName, pmuEvtPtr);
if (err != SUCCESS) {
delete pmuEvtPtr;
return nullptr;
}
return pmuEvtPtr;
}