* 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.Li
* Create: 2024-07-04
* Description: Provides the capability of parsing pointer events.
******************************************************************************/
#include "trace_point_parser.h"
using namespace KUNPENG_PMU;
using namespace pcerr;
const char *POINTER_FIELD_STR = "field:";
const char *POINTER_OFFSET_REGEX = "%*[^0-9]%i%*[;] %*[^0-9]%i%*[;] %*[^0-9]%i%*[;]";
static std::unordered_map<std::string, std::unordered_map<string, Field>> efMap;
static std::unordered_map<char *, std::string> dEvtMap;
static std::map<Field, SampleRawField *> fsrMap;
static std::unordered_map<std::string, std::string> formatMap;
static std::string GetFormatRealPath(const std::string &evtName) {
auto colonId = evtName.find(':');
if (colonId == string::npos) {
return {};
}
string eventName = evtName.substr(colonId + 1);
string systemName = evtName.substr(0, colonId);
const string &eventDir = GetTraceEventDir();
if (eventDir.empty()) {
return {};
}
string formatPath = eventDir + systemName + "/" + eventName + "/format";
return GetRealPath(formatPath);
}
bool TraceParser::IsNeedFormat(std::ifstream &file, const std::string &evtName) {
std::string realPath;
if (formatMap.find(evtName) != formatMap.end()) {
realPath = formatMap.at(evtName);
} else {
realPath = GetFormatRealPath(evtName);
formatMap.emplace(evtName, realPath);
}
if (!IsValidPath(realPath)) {
return false;
}
file.open(realPath);
if (!file.is_open()) {
return false;
}
return true;
}
void ParseFormatFile(ifstream &file, const std::string &evtName) {
string line;
std::unordered_map<string, Field> fnMap;
while (getline(file, line)) {
if (line.find(POINTER_FIELD_STR) == string::npos) {
continue;
}
auto sealId = line.find(';');
if (sealId == string::npos) {
continue;
}
Field field;
if (!sscanf(line.substr(sealId + 1).c_str(), POINTER_OFFSET_REGEX, &field.offset, &field.size,
&field.isSigned)) {
continue;
}
field.fieldStr = line.substr(strlen(POINTER_FIELD_STR) + 1, sealId - strlen(POINTER_FIELD_STR) - 1);
auto spaceId = field.fieldStr.find_last_of(' ');
auto braceId = field.fieldStr.find('[');
field.fieldName =
braceId == string::npos ? field.fieldStr.substr(spaceId + 1) : field.fieldStr.substr(spaceId + 1,
braceId - spaceId -
1);
fnMap.insert({field.fieldName, field});
}
efMap.insert({evtName, fnMap});
}
void TraceParser::ParserRawFormatData(struct PmuData *pd, KUNPENG_PMU::PerfRawSample *sample,
union KUNPENG_PMU::PerfEvent *event,
const std::string &evtName) {
ifstream file;
if (efMap.find(evtName) == efMap.end() && !IsNeedFormat(file, evtName)) {
pd->rawData = nullptr;
return;
}
auto ipsOffset = (unsigned long) offset(struct PerfRawSample, ips);
auto traceOffset = ipsOffset + sample->nr * (sizeof(unsigned long));
TraceRawData *trace = (TraceRawData *) ((char *) &event->sample.array + traceOffset);
if (trace->size == 0) {
pd->rawData = nullptr;
return;
}
pd->rawData = (SampleRawData *) malloc(sizeof(struct SampleRawData));
if (!pd->rawData) {
return;
}
pd->rawData->data = (char *) malloc(trace->size);
if (!pd->rawData->data) {
free(pd->rawData);
pd->rawData = nullptr;
return;
}
memcpy(pd->rawData->data, trace->data, trace->size);
dEvtMap.insert({pd->rawData->data, evtName});
if (efMap.find(evtName) != efMap.end()) {
return;
}
ParseFormatFile(file, evtName);
}
int CheckFieldArgs(char *data, const string &fieldName) {
if (data == nullptr) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS, "data cannot be nullptr.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
if (fieldName.empty()) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS, "fieldName cannot be empty.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
auto evtIt = dEvtMap.find(data);
if (evtIt == dEvtMap.end()) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS,
"The args data maybe have changed, can't find the event name which it is associated with.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
auto fieldIt = efMap.find(evtIt->second);
if (fieldIt == efMap.end()) {
New(LIBPERF_ERR_FIND_FIELD_LOSS, "unknown error, can't find the filed map.");
return LIBPERF_ERR_FIND_FIELD_LOSS;
}
if (fieldIt->second.find(fieldName) == fieldIt->second.end()) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS, "invalid fieldName, can't find it in format data.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;;
}
return SUCCESS;
}
template<typename T>
int CheckFieldArgs(char *data, const string &fieldName, T *value, uint32_t vSize) {
if (value == nullptr) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS, "value cannot be nullptr.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
if (vSize == 0) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS, "vSize cannot be zero.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
return CheckFieldArgs(data, fieldName);
}
template<typename T>
int TraceParser::ParseField(char *data, const std::string &fieldName, T *value, uint32_t vSize) {
int rt = CheckFieldArgs(data, fieldName, value, vSize);
if (rt != SUCCESS) {
return rt;
}
Field field = efMap.at(dEvtMap.at(data)).at(fieldName);
if (field.IsDataLoc()) {
int dataLoc;
memcpy(&dataLoc, data + field.offset, field.size);
char *dataName = data + (unsigned short) (dataLoc & 0xffff);
if (strlen(dataName) > vSize) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS,
"__dta_loc data requires " + std::to_string(strlen(dataName)) +
" bytes of memory, the value needs larger memory.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
memcpy(value, dataName, strlen(dataName) + 1);
return SUCCESS;
}
if (field.size > vSize) {
New(LIBPERF_ERR_INVALID_FIELD_ARGS,
"value requires " + std::to_string(field.size) +
" bytes of memory, the value needs larger memory.");
return LIBPERF_ERR_INVALID_FIELD_ARGS;
}
memcpy(value, data + field.offset, field.size);
New(SUCCESS);
return SUCCESS;
}
int TraceParser::ParseTraceData(char *data, const std::string &fieldName, void *value,
uint32_t vSize) {
return ParseField(data, fieldName, value, vSize);
}
void TraceParser::FreeTraceData(char *data) {
if (data == nullptr) {
return;
}
if (dEvtMap.find(data) != dEvtMap.end()) {
dEvtMap.erase(data);
}
free(data);
data = nullptr;
}
SampleRawField *TraceParser::GetSampleRawField(char *data, const std::string &fieldName) {
int ret = CheckFieldArgs(data, fieldName);
if (ret != SUCCESS) {
return nullptr;
}
Field field = efMap.at(dEvtMap.at(data)).at(fieldName);
if (fsrMap.find(field) == fsrMap.end()) {
auto *rawField = new SampleRawField();
rawField->fieldStr = new char[field.fieldStr.length() + 1];
rawField->fieldName = new char[field.fieldName.length() + 1];
memcpy(rawField->fieldStr, field.fieldStr.data(), field.fieldStr.length() + 1);
memcpy(rawField->fieldName, field.fieldName.data(), field.fieldName.length() + 1);
rawField->offset = field.size;
rawField->size = field.size;
rawField->isSigned = field.isSigned;
fsrMap.insert({field, rawField});
return rawField;
}
return fsrMap.at(field);
}
void TraceParser::FreeRawFieldMap() {
for (auto it = fsrMap.begin(); it != fsrMap.end(); ++it) {
if (!it->second) {
continue;
}
if (it->second->fieldName) {
delete [] it->second->fieldName;
it->second->fieldName = nullptr;
}
if (it->second->fieldStr) {
delete [] it->second->fieldStr;
it->second->fieldStr = nullptr;
}
delete it->second;
it->second = nullptr;
}
fsrMap.clear();
}