* 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.Zhang
* Create: 2024-04-03
* Description: functions for managing performance monitoring tasks, collecting data, and handling
* performance counters in the KUNPENG_PMU namespace
******************************************************************************/
#include <memory>
#include <algorithm>
#include <numeric>
#include <string>
#include <sys/resource.h>
#include "linked_list.h"
#include "cpu_map.h"
#include "process_map.h"
#include "pcerrc.h"
#include "pcerr.h"
#include "util_time.h"
#include "log.h"
#include "trace_point_parser.h"
#include "pmu_event_list.h"
#include "pmu_list.h"
#include "pfm_event.h"
#include "evt_list_default.h"
#ifdef BPF_ENABLED
#include "bpf/evt_list_bpf.h"
#endif
using namespace std;
using namespace pcerr;
namespace KUNPENG_PMU {
std::mutex PmuList::pmuListMtx;
std::mutex PmuList::dataEvtGroupListMtx;
std::mutex PmuList::dataListMtx;
std::mutex PmuList::dataParentMtx;
std::mutex PmuList::analysisStatusMtx;
int PmuList::CheckRlimit(const unsigned pd, const unsigned fdNum)
{
unsigned long extra = 50;
unsigned long curNeedFd = extra + fdNum;
struct rlimit currentlim;
if (getrlimit(RLIMIT_NOFILE, ¤tlim) == -1) {
return LIBPERF_ERR_RAISE_FD;
}
pmuNeedFdList[pd] = curNeedFd;
unsigned totalFd = 0;
for (auto item : pmuNeedFdList) {
totalFd += item.second;
}
if (currentlim.rlim_cur > totalFd) {
return SUCCESS;
}
struct rlimit rlim {
.rlim_cur = currentlim.rlim_max, .rlim_max = currentlim.rlim_max,
};
if (currentlim.rlim_cur < totalFd && totalFd < currentlim.rlim_max) {
rlim.rlim_cur = totalFd;
} else if (totalFd > currentlim.rlim_max) {
rlim.rlim_cur = totalFd;
rlim.rlim_max = totalFd;
}
if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) {
return LIBPERF_ERR_RAISE_FD;
} else {
return SUCCESS;
}
}
unsigned PmuList::CalRequireFd(unsigned cpuSize, unsigned proSize, const unsigned collectType)
{
unsigned fd = cpuSize * proSize;
if (collectType == SPE_SAMPLING) {
fd += fd;
}
return fd;
}
int PmuList::Register(const int pd, PmuTaskAttr* taskParam)
{
struct PmuTaskAttr* pmuTaskAttrHead = taskParam;
auto& evtData = GetDataList(pd);
if (pmuTaskAttrHead != nullptr) {
evtData.collectType = static_cast<PmuTaskType>(pmuTaskAttrHead->pmuEvt->collectType);
evtData.pd = pd;
}
unsigned fdNum = 0;
while (pmuTaskAttrHead) {
* Create cpu topology list
*/
std::vector<CpuPtr> cpuTopoList;
auto err = PrepareCpuTopoList(pd, pmuTaskAttrHead, cpuTopoList);
if (err != SUCCESS) {
return err;
}
* Create process topology list
*/
std::vector<ProcPtr> procTopoList;
err = PrepareProcTopoList(pmuTaskAttrHead, procTopoList, pd);
if (err != SUCCESS) {
return err;
}
fdNum += CalRequireFd(cpuTopoList.size(), procTopoList.size(), taskParam->pmuEvt->collectType);
#ifdef BPF_ENABLED
if (taskParam->pmuEvt->enableBpf) {
std::shared_ptr<EvtListBpf> evtList = std::make_shared<EvtListBpf>(
GetSymbolMode(pd), cpuTopoList, procTopoList, pmuTaskAttrHead->pmuEvt, pmuTaskAttrHead->groupId);
InsertEvtList(pd, evtList);
} else
#endif
{
std::shared_ptr<EvtListDefault> evtList = std::make_shared<EvtListDefault>(
GetSymbolMode(pd), cpuTopoList, procTopoList, pmuTaskAttrHead->pmuEvt, pmuTaskAttrHead->groupId);
evtList->SetBranchSampleFilter(GetBranchSampleFilter(pd));
InsertEvtList(pd, evtList);
}
pmuTaskAttrHead = pmuTaskAttrHead->next;
}
int symbolErrNo = InitSymbolRecordModule(pd, taskParam);
if (symbolErrNo != SUCCESS) {
pcerr::SetCustomErr(symbolErrNo, Perror());
return symbolErrNo;
}
int err;
if (!taskParam->pmuEvt->enableBpf) {
err = CheckRlimit(pd, fdNum);
if (err != SUCCESS) {
return err;
}
}
err = Init(pd);
if (err != SUCCESS)
{
return err;
}
this->OpenDummyEvent(taskParam, pd);
return SUCCESS;
}
int PmuList::EvtInit(const bool groupEnable, const std::shared_ptr<EvtList> evtLeader, const int pd, const std::shared_ptr<EvtList> &evtList)
{
auto err = evtList->Init(groupEnable, evtLeader);
if (err != SUCCESS) {
(void)evtList->Close();
return err;
}
err = AddToEpollFd(pd, evtList);
if (err != SUCCESS) {
(void)evtList->Close();
return err;
}
return SUCCESS;
}
static inline bool IsNotInitFatalErr(int err) {
return err == LIBPERF_ERR_OPEN_INVALID_FILE;
}
int PmuList::InitScanEvtLists(const int pd, std::vector<std::shared_ptr<EvtList>> &evtLists, EvtInitCtx &ctx)
{
for (auto &evtList : evtLists) {
const int gid = evtList->GetGroupId();
if (gid == -1) {
int err = EvtInit(false, nullptr, pd, evtList);
if (err != SUCCESS) {
if (!IsNotInitFatalErr(err)) {
return err;
}
ctx.lastErr = err;
ctx.removeEvt.insert(evtList.get());
continue;
} else {
ctx.oneEvtInitOk = true;
}
continue;
}
if (ctx.eventGroupInfoMap.find(gid) == ctx.eventGroupInfoMap.end()) {
int err = EvtInit(true, nullptr, pd, evtList);
if (err != SUCCESS) {
if (!IsNotInitFatalErr(err)) {
return err;
}
ctx.lastErr = err;
ctx.removeEvt.insert(evtList.get());
ctx.groupLeaderOk[gid] = false;
ctx.eventGroupInfoMap[gid].uncoreState = static_cast<UncoreState>(UncoreState::InitState);
continue;
} else {
ctx.eventGroupInfoMap[gid].evtLeader = evtList;
ctx.eventGroupInfoMap[gid].uncoreState = static_cast<UncoreState>(UncoreState::InitState);
ctx.groupLeaderOk[gid] = true;
ctx.oneEvtInitOk = true;
}
ctx.groupHasGroupedChild[gid] = false;
} else {
ctx.eventGroupInfoMap[gid].evtGroupChildList.push_back(evtList);
}
if (evtList->GetPmuType() == static_cast<PMU_TYPE>(UNCORE_TYPE) || evtList->GetPmuType() == static_cast<PMU_TYPE>(UNCORE_RAW_TYPE)) {
ctx.eventGroupInfoMap[gid].uncoreState = static_cast<UncoreState>(static_cast<int>(UncoreState::HasUncore) |
static_cast<int>(ctx.eventGroupInfoMap[gid].uncoreState));
} else {
ctx.eventGroupInfoMap[gid].uncoreState = static_cast<UncoreState>(static_cast<int>(UncoreState::HasUncore) &
static_cast<int>(ctx.eventGroupInfoMap[gid].uncoreState));
}
}
return SUCCESS;
}
int PmuList::InitGroupChildren(const int pd, EvtInitCtx &ctx)
{
for (auto &kv : ctx.eventGroupInfoMap) {
const int gid = kv.first;
auto &grpInfo = kv.second;
if (grpInfo.uncoreState == static_cast<UncoreState>(UncoreState::OnlyUncore)) {
return LIBPERF_ERR_INVALID_GROUP_ALL_UNCORE;
}
std::shared_ptr<EvtList> leader = nullptr;
auto itLeaderOk = ctx.groupLeaderOk.find(gid);
if (itLeaderOk != ctx.groupLeaderOk.end() && itLeaderOk->second) {
leader = grpInfo.evtLeader.lock();
}
for (auto &evtChild : grpInfo.evtGroupChildList) {
auto child = evtChild.lock();
if (!child) {
continue;
}
int err = SUCCESS;
if (grpInfo.uncoreState == static_cast<UncoreState>(UncoreState::HasUncore)) {
SetWarn(LIBPERF_WARN_INVALID_GROUP_HAS_UNCORE);
err = EvtInit(false, nullptr, pd, child);
} else {
if (leader) {
err = EvtInit(true, leader, pd, child);
if (err == SUCCESS) {
ctx.groupHasGroupedChild[gid] = true;
}
} else {
err = EvtInit(false, nullptr, pd, child);
}
}
if (err != SUCCESS) {
if (!IsNotInitFatalErr(err)) {
return err;
}
ctx.lastErr = err;
ctx.removeEvt.insert(child.get());
continue;
} else {
ctx.oneEvtInitOk = true;
}
}
}
return SUCCESS;
}
static inline std::string GetRemoveEvtNames(const std::unordered_set<const EvtList*>& removeEvt)
{
std::vector<std::string> names;
names.reserve(removeEvt.size());
for (auto p : removeEvt) {
if (!p) {
continue;
}
names.emplace_back(p->GetPmuEvtName());
}
std::string out;
for (size_t i = 0; i < names.size(); ++i) {
if (i) {
out += ",";
}
out += names[i];
}
return out;
}
int PmuList::Init(const int pd)
{
EvtInitCtx ctx;
auto &evtLists = GetEvtList(pd);
int err = InitScanEvtLists(pd, evtLists, ctx);
if (err != SUCCESS) {
return err;
}
err = InitGroupChildren(pd, ctx);
if (err != SUCCESS) {
return err;
}
const std::string removedNames = GetRemoveEvtNames(ctx.removeEvt);
if (!ctx.removeEvt.empty()) {
evtLists.erase(std::remove_if(evtLists.begin(), evtLists.end(), [&](const std::shared_ptr<EvtList> &p) {
return (!p) || (ctx.removeEvt.find(p.get()) != ctx.removeEvt.end());}), evtLists.end());
}
std::unordered_map<int, struct EventGroupInfo> processedGroupInfoMap;
for (auto &kv : ctx.eventGroupInfoMap) {
const int gid = kv.first;
auto &grpInfo = kv.second;
const bool leaderOk = ctx.groupLeaderOk.count(gid) && ctx.groupLeaderOk[gid];
const bool hasGroupedChild = ctx.groupHasGroupedChild.count(gid) && ctx.groupHasGroupedChild[gid];
auto leader = leaderOk ? grpInfo.evtLeader.lock() : nullptr;
if (leader && hasGroupedChild) {
EventGroupInfo newInfo = grpInfo;
newInfo.evtGroupChildList.clear();
for (auto &evt : grpInfo.evtGroupChildList) {
auto sp = evt.lock();
if (!sp) {
continue;
}
if (ctx.removeEvt.count(sp.get())) {
continue;
}
newInfo.evtGroupChildList.push_back(evt);
}
leader->SetGroupInfo(newInfo);
processedGroupInfoMap.emplace(gid, newInfo);
}
}
groupMapPtr eventDataEvtGroup = std::make_shared<std::unordered_map<int, EventGroupInfo>>(processedGroupInfoMap);
InsertDataEvtGroupList(pd, eventDataEvtGroup);
for (auto &evtList : evtLists) {
if (evtList) {
evtList->RemoveInitErr();
}
}
if (!ctx.oneEvtInitOk) {
return ctx.lastErr;
}
if (ctx.lastErr != SUCCESS) {
SetWarn(ctx.lastErr, "Initialization failed: " + removedNames);
}
return SUCCESS;
}
int PmuList::Start(const int pd)
{
auto pmuList = GetEvtList(pd);
for (auto item: pmuList) {
auto err = item->Start();
if (err != SUCCESS) {
return err;
}
}
return SUCCESS;
}
int PmuList::Pause(const int pd)
{
auto pmuList = GetEvtList(pd);
for (auto item: pmuList) {
auto err = item->Pause();
if (err != SUCCESS) {
return err;
}
}
return SUCCESS;
}
std::pair<bool, std::shared_ptr<EvtList>> PmuList::GetEvtGroupState(const int groupId, std::shared_ptr<EvtList> evtList, groupMapPtr eventGroupInfoMap)
{
if (groupId == -1 || eventGroupInfoMap == nullptr) {
return std::make_pair(false, nullptr);
}
if (evtList == (*eventGroupInfoMap)[groupId].evtLeader.lock()) {
return std::make_pair(false, nullptr);
} else {
if ((*eventGroupInfoMap)[groupId].uncoreState == static_cast<UncoreState>(UncoreState::HasUncore)) {
return std::make_pair(false, nullptr);
} else {
return std::make_pair(true, (*eventGroupInfoMap)[groupId].evtLeader.lock());
}
}
}
void PmuList::AddNewProcess(const unsigned &pd, int pid)
{
if (pid <= 0) {
return;
}
auto& procList = pmuProcList[pd];
ProcTopology* topology = GetProcTopology(pid);
if (topology == nullptr) {
return;
}
procList.emplace_back(shared_ptr<ProcTopology>(topology, FreeProcTopo));
auto eventList = GetEvtList(pd);
for (const auto& evtList : eventList) {
auto groupId = evtList->GetGroupId();
auto evtGroupInfo = GetEvtGroupState(groupId, evtList, GetDataEvtGroupList(pd));
evtList->AddNewProcess(pid, evtGroupInfo.first, evtGroupInfo.second);
}
}
void PmuList::ClearExitFd(const unsigned &pd)
{
if (pmuProcList.find(pd) == pmuProcList.end()) {
return;
}
auto &pidList = pmuProcList[pd];
if (pidList.size() == 1 && pidList[0]->tid == -1) {
return;
}
std::set<int> noProcList;
for (const auto &it : pidList) {
if (it->isMain) {
continue;
}
std::string path = "/proc/" + std::to_string(it->tid);
if (!ExistPath(path)) {
noProcList.insert(it->tid);
}
}
if (noProcList.empty()) {
return;
}
auto eventList = GetEvtList(pd);
for (const auto& evtList : eventList) {
evtList->ClearExitFd(noProcList);
}
noProcList.clear();
}
std::vector<PmuData>& PmuList::Read(const int pd)
{
auto& evtData = GetDataList(pd);
if (evtData.data.empty()) {
auto err = ReadDataToBuffer(pd);
if (err != SUCCESS) {
return userDataList[nullptr].data;
}
}
auto& userData = ExchangeToUserData(pd);
return userData;
}
static void TrimKernelStack(PmuData &data)
{
auto stack = data.stack;
while (stack != nullptr && stack->symbol != nullptr) {
if (strcmp(stack->symbol->module, "[kernel]") == 0) {
stack = stack->next;
continue;
}
data.stack = stack;
break;
}
}
void SortTwoVector(std::vector<PmuData>& pmuData, std::vector<PerfSampleIps>& sampleIps)
{
std::vector<size_t> indices(pmuData.size());
std::iota(indices.begin(), indices.end(), 0);
std::stable_sort(indices.begin(), indices.end(), [&pmuData](size_t a, size_t b){
if (pmuData[a].tid == pmuData[b].tid) {
return pmuData[a].ts < pmuData[b].ts;
}
return pmuData[a].tid < pmuData[b].tid;
});
std::vector<PmuData> sortedPmuData;
std::vector<PerfSampleIps> sortedSampleIps;
size_t size = pmuData.size();
sortedPmuData.reserve(size);
sortedSampleIps.reserve(size);
for (size_t i = 0; i < size; ++i) {
sortedPmuData.emplace_back(std::move(pmuData[indices[i]]));
sortedSampleIps.emplace_back(std::move(sampleIps[indices[i]]));
}
pmuData = std::move(sortedPmuData);
sampleIps = std::move(sortedSampleIps);
}
void HandleBlockData(std::vector<PmuData>& pmuData, std::vector<PerfSampleIps>& sampleIps,
SymbolMode symMode,std::vector<PmuSwitchData>& switchData)
{
std::sort(switchData.begin(), switchData.end(), [](const PmuSwitchData& a, const PmuSwitchData& b) {
if (a.tid == b.tid) {
return a.ts < b.ts;
}
return a.tid < b.tid;
});
std::unordered_map<int, std::vector<int64_t>> tidToOffTimeStamps;
int64_t outTime = 0;
int prevTid = -1;
for (const auto& item : switchData) {
if (item.swOut) {
outTime = item.ts;
prevTid = item.tid;
DBG_PRINT("Switch out: tid=%d, ts=%llu\n", item.tid, item.ts);
} else {
if (prevTid == -1) {
DBG_PRINT("Ignoring first sched_in event: tid=%d, ts=%ld\n", item.tid, item.ts);
continue;
}
if (prevTid == item.tid && outTime > 0) {
tidToOffTimeStamps[item.tid].emplace_back(item.ts - outTime);
DBG_PRINT("Switch in: tid=%d, ts=%llu, offTime=%llu\n", item.tid, item.ts, item.ts - outTime);
outTime = 0;
}
}
}
SortTwoVector(pmuData, sampleIps);
int csCnt = 0;
int64_t prevTs = 0;
int64_t currentTs = 0;
int64_t curPeriod = 0;
int currentTid = -1;
for (auto& item : pmuData) {
if (currentTid != item.tid) {
currentTid = item.tid;
csCnt = 0;
prevTs = 0;
currentTs = 0;
curPeriod = 0;
DBG_PRINT("New tid encountered: tid=%d\n", currentTid);
}
if (strcmp(item.evt, "context-switches") == 0) {
if (symMode != NO_SYMBOL_RESOLVE) {
TrimKernelStack(item);
}
if (currentTs == 0) {
currentTs = item.ts;
DBG_PRINT("Ignoring first cycles event for tid=%d\n", item.tid);
continue;
}
if (csCnt < tidToOffTimeStamps[item.tid].size()) {
item.period = tidToOffTimeStamps[item.tid][csCnt] * curPeriod / (currentTs - prevTs);
DBG_PRINT("Context switch: ts=%llu, tid=%d, period=%llu\n", item.ts, item.tid, item.period);
csCnt++;
}
} else {
prevTs = currentTs;
currentTs = item.ts;
curPeriod = item.period;
}
}
}
int PmuList::ReadDataToBuffer(const int pd)
{
auto& evtData = GetDataList(pd);
evtData.pd = pd;
evtData.collectType = static_cast<PmuTaskType>(GetTaskType(pd));
auto ts = GetCurrentTime();
auto eventList = GetEvtList(pd);
for (auto item: eventList) {
item->SetTimeStamp(ts);
auto err = item->Read(evtData);
if (err != SUCCESS) {
return err;
}
}
this->ClearExitFd(pd);
return SUCCESS;
}
int PmuList::AppendData(PmuData* fromData, PmuData** toData, int& len)
{
if (toData == nullptr || fromData == nullptr) {
return LIBPERF_ERR_INVALID_PMU_DATA;
}
lock_guard<mutex> lg(dataListMtx);
auto findFromData = userDataList.find(fromData);
if (findFromData == userDataList.end()) {
return LIBPERF_ERR_INVALID_PMU_DATA;
}
if (*toData == nullptr) {
EventData newData = findFromData->second;
len = newData.data.size();
auto pData = newData.data.data();
userDataList[pData] = move(newData);
*toData = pData;
return SUCCESS;
}
auto findToData = userDataList.find(*toData);
if (findToData == userDataList.end()) {
return LIBPERF_ERR_INVALID_PMU_DATA;
}
auto& dataVec = findToData->second.data;
auto& ipsVec = findToData->second.sampleIps;
dataVec.insert(dataVec.end(), findFromData->second.data.begin(), findFromData->second.data.end());
ipsVec.insert(ipsVec.end(), findFromData->second.sampleIps.begin(), findFromData->second.sampleIps.end());
len = dataVec.size();
if (*toData != dataVec.data()) {
auto newDataPtr = dataVec.data();
userDataList[newDataPtr] = move(findToData->second);
userDataList.erase(*toData);
*toData = newDataPtr;
}
return SUCCESS;
}
void PmuList::StoreSplitData(const unsigned pd, pair<unsigned, char**>& previousEventList,
unordered_map<string, char*>& eventSplitMap)
{
lock_guard<mutex> lg(dataParentMtx);
parentEventMap.emplace(pd, move(eventSplitMap));
previousEventMap.emplace(pd, move(previousEventList));
}
void PmuList::Close(const int pd)
{
EraseDummyEvent(pd);
auto evtList = GetEvtList(pd);
for (auto item: evtList) {
item->Close();
}
EraseEvtList(pd);
EraseSymModeList(pd);
ErasePpidList(pd);
EraseProcptrList(pd);
EraseDataList(pd);
EraseDataEvtGroupList(pd);
RemoveEpollFd(pd);
EraseSpeCpu(pd);
EraseParentEventMap(pd);
EraseUnUseFd(pd);
SymResolverDestroy();
PmuEventListFree();
TraceParser::FreeRawFieldMap();
}
int PmuList::NewPd()
{
lock_guard<mutex> lg(pmuListMtx);
if (maxPd == std::numeric_limits<unsigned>::max()) {
unsigned availPd = 0;
auto findPd = pmuList.find(availPd);
while (findPd != pmuList.end()) {
++availPd;
findPd = pmuList.find(availPd);
if (availPd == std::numeric_limits<unsigned>::max()) {
return -1;
}
}
maxPd = availPd;
} else {
maxPd++;
}
return maxPd;
}
bool PmuList::AllPmuDead(const int pd)
{
auto epollFd = GetEpollFd(pd);
if (epollFd == -1) {
return true;
}
auto epollEvents = GetEpollEvents(epollFd);
epoll_wait(epollFd, epollEvents.data(), epollEvents.size(), 0);
for (auto& evt: epollEvents) {
if (!(evt.events & EPOLLHUP)) {
return false;
}
}
return true;
}
bool PmuList::IsPdAlive(const int pd) const
{
lock_guard<mutex> lg(pmuListMtx);
return pmuList.find(pd) != pmuList.end();
}
void PmuList::FreeData(PmuData* pmuData)
{
EraseUserData(pmuData);
}
int PmuList::GetTaskType(const int pd) const
{
lock_guard<mutex> lg(pmuListMtx);
auto findEvtList = pmuList.find(pd);
if (findEvtList == pmuList.end()) {
return -1;
}
if (findEvtList->second.empty()) {
return -1;
}
return findEvtList->second[0]->GetEvtType();
}
int PmuList::GetBlockedSampleState(const int pd) const
{
lock_guard<mutex> lg(pmuListMtx);
auto findEvtList = pmuList.find(pd);
if (findEvtList == pmuList.end()) {
return -1;
}
if (findEvtList->second.empty()) {
return -1;
}
return findEvtList->second[0]->GetBlockedSample();
}
void PmuList::InsertEvtList(const unsigned pd, std::shared_ptr<EvtList> evtList)
{
lock_guard<mutex> lg(pmuListMtx);
pmuList[pd].push_back(evtList);
}
std::vector<std::shared_ptr<EvtList>>& PmuList::GetEvtList(const unsigned pd)
{
lock_guard<mutex> lg(pmuListMtx);
return pmuList[pd];
}
void PmuList::EraseEvtList(const unsigned pd)
{
lock_guard<mutex> lg(pmuListMtx);
pmuList.erase(pd);
}
void PmuList::EraseSymModeList(const unsigned pd)
{
lock_guard<mutex> lg(dataListMtx);
symModeList.erase(pd);
}
void PmuList::ErasePpidList(const unsigned pd)
{
ppidList.erase(pd);
}
void PmuList::EraseProcptrList(const unsigned pd)
{
pmuProcList.erase(pd);
}
void PmuList::EraseUnUseFd(const unsigned pd) {
pmuNeedFdList.erase(pd);
}
void PmuList::InsertDataEvtGroupList(const unsigned pd, groupMapPtr evtGroupList)
{
lock_guard<mutex> lg(dataEvtGroupListMtx);
dataEvtGroupList[pd] = evtGroupList;
}
void PmuList::EraseDataEvtGroupList(const unsigned pd)
{
lock_guard<mutex> lg(dataEvtGroupListMtx);
dataEvtGroupList.erase(pd);
}
groupMapPtr& PmuList::GetDataEvtGroupList(const unsigned pd)
{
lock_guard<mutex> lg(dataEvtGroupListMtx);
return dataEvtGroupList[pd];
}
void PmuList::EraseParentEventMap(const unsigned pd)
{
lock_guard<mutex> lg(dataParentMtx);
auto iter = parentEventMap.find(pd);
if (iter != parentEventMap.end()) {
parentEventMap.at(pd).clear();
parentEventMap.erase(iter);
}
auto preIter = previousEventMap.find(pd);
if (preIter != previousEventMap.end()) {
auto pair = previousEventMap.at(pd);
for (int i = 0; i < pair.first; i++) {
delete[] pair.second[i];
}
delete[] pair.second;
previousEventMap.erase(preIter);
}
}
EventData& PmuList::GetDataList(const unsigned pd)
{
lock_guard<mutex> lg(dataListMtx);
return dataList[pd];
}
void PmuList::EraseDataList(const unsigned pd)
{
lock_guard<mutex> lg(dataListMtx);
dataList.erase(pd);
for (auto iter = userDataList.begin(); iter != userDataList.end();) {
if (iter->second.pd == pd) {
iter = userDataList.erase(iter);
} else {
++iter;
}
}
}
void PmuList::FillStackInfo(EventData& eventData)
{
auto symMode = symModeList[eventData.pd];
for (size_t i = 0; i < eventData.data.size(); ++i) {
if (GetAnalysisStatus(eventData.pd) == STOP_RESOLVE) {
break;
}
auto& pmuData = eventData.data[i];
auto& ipsData = eventData.sampleIps[i];
if (symMode == RESOLVE_ELF) {
SymResolverRecordModuleNoDwarf(pmuData.pid);
} else if (symMode == RESOLVE_ELF_DWARF) {
SymResolverRecordModule(pmuData.pid);
} else if (symMode == RESOLVE_DELAY_ELF) {
SymResolverRecordModuleNoDwarf(pmuData.pid);
continue;
} else if (symMode == RESOLVE_DELAY_DWARF) {
SymResolverRecordModule(pmuData.pid);
continue;
} else {
continue;
}
if (pmuData.stack == nullptr) {
pmuData.stack = StackToHash(pmuData.pid, ipsData.ips.data(), ipsData.ips.size());
}
}
int err = Perrorno();
if (err < LIBPERF_ERR_NO_AVAIL_PD && err >= LIBSYM_ERR_BASE) {
pcerr::SetWarn(err, Perror());
New(SUCCESS);
}
}
int PmuList::ResolvePmuDataSymbol(struct PmuData* iPmuData)
{
if (iPmuData == nullptr) {
New(LIBPERF_ERR_INVALID_PMU_DATA, "ipmuData is nullptr");
return LIBPERF_ERR_INVALID_PMU_DATA;
}
auto userData = userDataList.find(iPmuData);
if (userData == userDataList.end()) {
New(LIBPERF_ERR_PMU_DATA_NO_FOUND, "ipmuData isn't in userDataList");
return LIBPERF_ERR_PMU_DATA_NO_FOUND;
}
auto& eventData = userDataList[iPmuData];
for (size_t i = 0; i < eventData.data.size(); ++i) {
if (GetAnalysisStatus(eventData.pd) == STOP_RESOLVE) {
break;
}
auto& pmuData = eventData.data[i];
auto& ipsData = eventData.sampleIps[i];
if (pmuData.stack == nullptr) {
pmuData.stack = StackToHash(pmuData.pid, ipsData.ips.data(), ipsData.ips.size());
}
}
if (GetBlockedSampleState(eventData.pd) == 1) {
for (auto& item : eventData.data) {
if (strcmp(item.evt, "context-switches") == 0) {
TrimKernelStack(item);
}
}
}
New(SUCCESS);
return SUCCESS;
}
std::vector<PerfRecordSample> PmuList::GetMetaData(PmuData* pmuData) const
{
auto findData = userDataList.find(pmuData);
if (findData == userDataList.end()) {
return vector<PerfRecordSample>();
}
return findData->second.metaData;
}
void PmuList::AggregateData(const std::vector<PmuData>& evData, std::vector<PmuData>& newEvData)
{
map<std::tuple<string, int, unsigned>, PmuData> mergedMap;
for (auto& data: evData) {
auto key = std::make_tuple(
data.evt, data.tid, data.cpu);
if (mergedMap.find(key) == mergedMap.end()) {
mergedMap[key] = data;
} else {
mergedMap[key].count += data.count;
}
}
for (auto& evtData: mergedMap) {
newEvData.push_back(evtData.second);
}
}
void PmuList::AggregateUncoreData(const unsigned pd, const vector<PmuData>& evData, vector<PmuData>& newEvData)
{
auto parentMap = parentEventMap.at(pd);
unordered_map<string, PmuData> dataMap;
vector<string> dataMapKeys;
set<string> statSubEvents;
for (auto& pmuData: evData) {
auto parentName = parentMap.at(pmuData.evt);
if (strcmp(parentName, pmuData.evt) == 0) {
if (dataMap.size() == 1) {
auto it = dataMap.begin();
newEvData.emplace_back(it->second);
dataMap.erase(it);
dataMapKeys.clear();
}
newEvData.emplace_back(pmuData);
continue;
}
if (dataMap.find(parentName) == dataMap.end()) {
dataMap[parentName] = pmuData;
dataMap[parentName].evt = parentMap.at(pmuData.evt);
dataMap[parentName].cpu = 0;
dataMap[parentName].cpuTopo = nullptr;
dataMapKeys.push_back(parentName);
} else {
if (statSubEvents.find(pmuData.evt) == statSubEvents.end()) {
dataMap.at(parentMap.at(pmuData.evt)).count += pmuData.count;
}
}
statSubEvents.insert(pmuData.evt);
}
for (auto& key: dataMapKeys) {
newEvData.emplace_back(dataMap.at(key));
}
}
vector<PmuData>& PmuList::ExchangeToUserData(const unsigned pd)
{
lock_guard<mutex> lg(dataListMtx);
if (dataList.count(pd) == 0) {
return GetPreviousData(pd);
}
auto& evData = dataList[pd];
if (GetTaskType(pd) == COUNTING) {
std::vector<PmuData> newPmuData;
AggregateUncoreData(pd, evData.data, newPmuData);
EventData newEvData = {
.pd = pd,
.collectType = COUNTING,
.data = move(newPmuData),
};
auto inserted = userDataList.emplace(newEvData.data.data(), move(newEvData));
dataList.erase(pd);
return inserted.first->second.data;
} else {
FillStackInfo(evData);
if (GetBlockedSampleState(pd) == 1) {
auto symMode = symModeList[evData.pd];
HandleBlockData(evData.data, evData.sampleIps, symMode, evData.switchData);
}
auto pData = evData.data.data();
auto inserted = userDataList.emplace(pData, move(evData));
dataList.erase(pd);
return inserted.first->second.data;
}
}
void PmuList::EraseUserData(PmuData* pmuData)
{
lock_guard<mutex> lg(dataListMtx);
auto findData = userDataList.find(pmuData);
if (findData == userDataList.end()) {
return;
}
if (findData->second.collectType == SAMPLING) {
for (auto &extMem : findData->second.extPool) {
if (extMem->branchRecords) {
delete[] extMem->branchRecords;
}
delete extMem;
}
} else if (findData->second.collectType == SPE_SAMPLING) {
for (auto &extMem : findData->second.extPool) {
delete[] extMem;
}
}
for (auto pd: findData->second.data) {
if (pd.rawData != nullptr) {
TraceParser::FreeTraceData(pd.rawData->data);
free(pd.rawData);
pd.rawData = nullptr;
}
}
userDataList.erase(pmuData);
}
int PmuList::GetHistoryData(const int pd, std::vector<PmuData>& aggregatedData)
{
lock_guard<mutex> lg(dataListMtx);
std::vector<PmuData> mergedData;
for (const auto& pair: userDataList) {
if (pair.second.pd == pd && pair.second.collectType == COUNTING) {
mergedData.insert(mergedData.end(), pair.second.data.begin(), pair.second.data.end());
}
}
AggregateData(mergedData, aggregatedData);
return aggregatedData.size();
}
std::vector<PmuData>& PmuList::GetPreviousData(const unsigned pd)
{
std::vector<PmuData>* lastData = nullptr;
int64_t maxTs = 0;
for (auto& pair: userDataList) {
if (pair.second.pd == pd && !pair.second.data.empty() && pair.second.data[0].ts > maxTs) {
maxTs = pair.second.data[0].ts;
lastData = &pair.second.data;
}
}
if (lastData != nullptr) {
return *lastData;
}
throw runtime_error("");
}
int PmuList::AddToEpollFd(const int pd, const std::shared_ptr<EvtList>& evtList)
{
lock_guard<mutex> lg(pmuListMtx);
int epollFd = 0;
auto findFd = epollList.find(pd);
if (findFd == epollList.end()) {
epollFd = epoll_create1(0);
if (epollFd < 0) {
return LIBPERF_ERR_FAIL_LISTEN_PROC;
}
epollList[pd] = epollFd;
} else {
epollFd = findFd->second;
}
auto& epollEvtList = epollEvents[epollFd];
for (auto fd: evtList->GetFdList()) {
epollEvtList.emplace_back(epoll_event{0});
auto& epollEvt = epollEvtList.back();
epollEvt.events = EPOLLIN | EPOLLRDHUP;
epollEvt.data.fd = fd;
auto ret = epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &epollEvt);
if (ret != 0) {
return LIBPERF_ERR_FAIL_LISTEN_PROC;
}
}
return SUCCESS;
}
void PmuList::RemoveEpollFd(const int pd)
{
lock_guard<mutex> lg(pmuListMtx);
auto findFd = epollList.find(pd);
if (findFd != epollList.end()) {
close(findFd->second);
epollEvents.erase(findFd->second);
epollList.erase(pd);
}
}
int PmuList::GetEpollFd(const int pd)
{
lock_guard<mutex> lg(pmuListMtx);
auto findFd = epollList.find(pd);
if (findFd != epollList.end()) {
return findFd->second;
}
return -1;
}
std::vector<epoll_event>& PmuList::GetEpollEvents(const int epollFd)
{
lock_guard<mutex> lg(pmuListMtx);
auto findEvts = epollEvents.find(epollFd);
if (findEvts != epollEvents.end()) {
return findEvts->second;
}
throw runtime_error("cannot find epoll events.");
}
bool PmuList::IsCpuInList(const int& cpu) const
{
lock_guard<mutex> lg(pmuListMtx);
for (auto cpuList: speCpuList) {
if (cpuList.second.find(cpu) != cpuList.second.end()) {
return true;
}
}
return false;
}
void PmuList::AddSpeCpu(const unsigned& pd, const int& cpu)
{
lock_guard<mutex> lg(pmuListMtx);
speCpuList[pd].insert(cpu);
}
void PmuList::EraseSpeCpu(const unsigned& pd)
{
lock_guard<mutex> lg(pmuListMtx);
speCpuList.erase(pd);
}
int PmuList::PrepareCpuTopoList(
const unsigned& pd, PmuTaskAttr* pmuTaskAttrHead, std::vector<CpuPtr>& cpuTopoList)
{
for (int cpuId : pmuTaskAttrHead->cpuList) {
if (pmuTaskAttrHead->pmuEvt->collectType == SPE_SAMPLING && IsCpuInList(cpuId)) {
return LIBPERF_ERR_DEVICE_BUSY;
}
struct CpuTopology* cpuTopo = GetCpuTopology(cpuId);
if (pmuTaskAttrHead->pmuEvt->collectType == SPE_SAMPLING) {
AddSpeCpu(pd, cpuId);
}
cpuTopoList.emplace_back(shared_ptr<CpuTopology>(cpuTopo));
}
return SUCCESS;
}
int PmuList::PrepareProcTopoList(PmuTaskAttr* pmuTaskAttrHead, std::vector<ProcPtr>& procTopoList, const int pd)
{
if (pmuTaskAttrHead->pidList.empty() || (pmuTaskAttrHead->pidList.size() == 1 && pmuTaskAttrHead->pidList[0] == -1)) {
struct ProcTopology* procTopo = GetProcTopology(-1);
if (procTopo == nullptr) {
New(LIBPERF_ERR_FAIL_GET_PROC);
return LIBPERF_ERR_FAIL_GET_PROC;
}
procTopo->isMain = true;
procTopoList.emplace_back(unique_ptr<ProcTopology, void (*)(ProcTopology*)>(procTopo, FreeProcTopo));
return SUCCESS;
}
if (pmuProcList.find(pd) != pmuProcList.end()) {
procTopoList = pmuProcList[pd];
return SUCCESS;
}
for (int masterPid : pmuTaskAttrHead->pidList) {
int numChild = 0;
int* childTidList = GetChildTid(masterPid, &numChild);
if (childTidList == nullptr) {
New(LIBPERF_ERR_INVALID_PID);
return LIBPERF_ERR_INVALID_PID;
}
bool foundProc = false;
for (int j = 0; j < numChild; j++) {
struct ProcTopology* procTopo = GetProcTopology(childTidList[j]);
if (procTopo == nullptr) {
SetWarn(LIBPERF_WARN_FAIL_GET_PROC, "process not found: " + std::to_string(childTidList[j]));
continue;
}
procTopo->isMain = (masterPid == procTopo->tid || masterPid == 0);
foundProc = true;
DBG_PRINT("Add to proc map: %d\n", childTidList[j]);
procTopoList.emplace_back(shared_ptr<ProcTopology>(procTopo, FreeProcTopo));
}
delete[] childTidList;
if (!foundProc) {
New(LIBPERF_ERR_FAIL_GET_PROC, "process not found: " + std::to_string(masterPid));
return LIBPERF_ERR_FAIL_GET_PROC;
}
}
pmuProcList[pd] = procTopoList;
return SUCCESS;
}
void PmuList::SetSymbolMode(const int pd, const SymbolMode& mode)
{
lock_guard<mutex> lg(dataListMtx);
symModeList[pd] = mode;
}
void PmuList::SetBranchSampleFilter(const int pd, const unsigned long& branchSampleFilter)
{
lock_guard<mutex> lg(dataListMtx);
branchSampleFilterList[pd] = branchSampleFilter;
}
void PmuList::SetAnalysisStatus(const int pd, const unsigned status)
{
lock_guard<mutex> lg(analysisStatusMtx);
analysisStatusList[pd] = status;
}
SymbolMode PmuList::GetSymbolMode(const unsigned pd)
{
lock_guard<mutex> lg(dataListMtx);
return symModeList[pd];
}
unsigned long PmuList::GetBranchSampleFilter(const unsigned pd)
{
lock_guard<mutex> lg(dataListMtx);
return branchSampleFilterList[pd];
}
unsigned PmuList::GetAnalysisStatus(const int pd)
{
lock_guard<mutex> lg(analysisStatusMtx);
return analysisStatusList[pd];
}
void PmuList::OpenDummyEvent(KUNPENG_PMU::PmuTaskAttr* taskParam, const unsigned pd)
{
if (!taskParam->pmuEvt->includeNewFork) {
return;
}
if (taskParam->pmuEvt->collectType != COUNTING) {
return;
}
if (taskParam->pidList.empty()) {
return;
}
auto* dummyEvent = new DummyEvent(pd, ppidList.at(pd));
dummyEvent->ObserverForkThread();
dummyList[pd] = dummyEvent;
}
void PmuList::FillPidList(const unsigned pd, unsigned numPid, int *pidList)
{
std::vector<pid_t> ppids;
for (int i = 0; i < numPid; i++) {
ppids.push_back(pidList[i]);
}
ppidList[pd] = ppids;
}
bool PmuList::IsAllPidExit(const unsigned pd)
{
auto& pidList = this->ppidList[pd];
if (pidList.empty()) {
return false;
}
int exitPidNum = 0;
for (const auto& pid: pidList) {
std::string path = "/proc/" + std::to_string(pid);
if (!ExistPath(path)) {
exitPidNum++;
}
}
if (exitPidNum == pidList.size()) {
return true;
}
return false;
}
void PmuList::EraseDummyEvent(const unsigned pd)
{
lock_guard<mutex> lg(pmuListMtx);
if (dummyList.find(pd) != dummyList.end()) {
DummyEvent* pEvent = dummyList.at(pd);
dummyList.erase(pd);
delete pEvent;
}
}
int PmuList::InitSymbolRecordModule(const unsigned pd, PmuTaskAttr* taskParam)
{
if (taskParam->pmuEvt->collectType == COUNTING) {
return SUCCESS;
}
if (taskParam->pmuEvt->excludeKernel != 1) {
int ret = SymResolverRecordKernel();
if (ret != SUCCESS) {
return ret;
}
}
std::vector<int> pidList = this->ppidList[pd];
if (pidList.empty()) {
return SUCCESS;
}
if (taskParam->pmuEvt->enableOnExec) {
return SUCCESS;
}
if (this->symModeList[pd] == RESOLVE_ELF) {
for (const auto& pid: pidList) {
int rt = SymResolverRecordModuleNoDwarf(pid);
if (rt != SUCCESS) {
return rt;
}
}
}
if (this->symModeList[pd] == RESOLVE_ELF_DWARF || this->symModeList[pd] == NO_SYMBOL_RESOLVE) {
for (const auto& pid: pidList) {
int rt = SymResolverRecordModule(pid);
if (rt != SUCCESS) {
return rt;
}
}
}
return SUCCESS;
}
PmuData* PmuList::RegisterDriverHandle(EventData&& newData)
{
std::lock_guard<std::mutex> lg(dataListMtx);
if (newData.data.empty()) {
return nullptr;
}
PmuData* handle = newData.data.data();
userDataList.emplace(handle, std::move(newData));
return handle;
}
}