* 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: 2025-05-07
* Description: sample cpu freq.
******************************************************************************/
#include "cpu_freq.h"
#include "pmu.h"
#include "pcerr.h"
using namespace pcerr;
CpuFreqManager* CpuFreqManager::instance = nullptr;
std::mutex CpuFreqManager::singleMutex;
std::mutex CpuFreqManager::initMutex;
std::vector<PmuCpuFreqDetail> CpuFreqManager::freqDetailList;
bool CpuFreqManager::hasInit = false;
PmuCpuFreqDetail* PmuReadCpuFreqDetail(unsigned* cpuNum) {
auto& ds = CpuFreqManager::GetCpuFreqDetail();
*cpuNum = ds.size();
return ds.data();
}
int PmuOpenCpuFreqSampling(unsigned period) {
return CpuFreqManager::GetInstance()->InitCpuFreqSampling(period);
}
void PmuCloseCpuFreqSampling() {
CpuFreqManager::Clear();
}
void CpuFreqManager::Clear() {
std::lock_guard<std::mutex> lock(singleMutex);
if (instance == nullptr) {
return;
}
delete instance;
instance = nullptr;
}
CpuFreqManager* CpuFreqManager::GetInstance() {
if (instance == nullptr) {
std::lock_guard<std::mutex> lock(singleMutex);
if(instance == nullptr) {
instance = new CpuFreqManager();
}
}
return instance;
}
int CpuFreqManager::CheckCpuFreqIsExist() {
for(int cpuId = 0; cpuId < MAX_CPU_NUM; cpuId++) {
int64_t freq = PmuGetCpuFreq(cpuId);
if (freq == -1 ) {
return -1;
}
}
return 0;
}
int CpuFreqManager::CheckSleepPeriod(unsigned period) {
if (period == 0 || period > 10000) {
New(LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD, "invalid period, the period must be less than 10000ms and greater than 0ms");
return LIBPERF_ERR_INVALID_CPU_FREQ_PERIOD;
}
return SUCCESS;
}
int CpuFreqManager::InitCpuFreqSampling(unsigned period) {
this->isEnable = true;
if (hasInit) {
return 0;
}
std::lock_guard<std::mutex> lock(initMutex);
if (CheckSleepPeriod(period) != 0) {
return -1;
}
if (CheckCpuFreqIsExist() != 0) {
return -1;
}
this->sleepPeriod = period * 1000;
this->cpuFreqThread = std::thread([this]() {
while (!isEnd) {
if (!isEnable) {
continue;
}
std::lock_guard<std::mutex> lock(mapMutex);
for (int cpu = 0; cpu < MAX_CPU_NUM; cpu++) {
int64_t freq = PmuGetCpuFreq(cpu);
if (freq == -1) {
continue;
}
if (this->freqListMap.find(cpu) != this->freqListMap.end()) {
this->freqListMap[cpu].push_back(freq);
} else {
std::vector<int64_t> freqList = {freq};
this->freqListMap.insert({cpu, freqList});
}
}
usleep(this->sleepPeriod);
}
});
hasInit = true;
return 0;
}
void CpuFreqManager::CalFreqDetail() {
isEnable = false;
std::lock_guard<std::mutex> lock(mapMutex);
if(!this->freqListMap.empty()) {
uint64_t maxFreq, minFreq, sumFreq;
for (int cpuId = 0; cpuId < MAX_CPU_NUM; cpuId++) {
std::vector<int64_t> freqList;
minFreq = 0;
maxFreq = 0;
sumFreq = 0;
if (this->freqListMap.find(cpuId) != this->freqListMap.end()) {
minFreq = UINT64_MAX;
freqList = freqListMap[cpuId];
}
for (const auto& curFreq: freqList) {
minFreq = minFreq > curFreq ? curFreq : minFreq;
maxFreq = maxFreq > curFreq ? maxFreq : curFreq;
sumFreq += curFreq;
}
uint64_t avgFreq = sumFreq / freqList.size();
PmuCpuFreqDetail detail = {.cpuId=cpuId, .minFreq=minFreq, .maxFreq=maxFreq, .avgFreq=avgFreq};
freqDetailList.push_back(detail);
}
freqListMap.clear();
} else {
GetCurFreqDetail();
}
isEnable = true;
}
void CpuFreqManager::GetCurFreqDetail() {
for(int cpuId = 0; cpuId < MAX_CPU_NUM; cpuId++) {
uint64_t freq = PmuGetCpuFreq(cpuId);
if (freq == -1) {
freq = 0;
}
PmuCpuFreqDetail detail = {.cpuId=cpuId, .minFreq=freq, .maxFreq=freq, .avgFreq=freq};
freqDetailList.push_back(detail);
}
}
std::vector<PmuCpuFreqDetail>& CpuFreqManager::GetCpuFreqDetail() {
std::lock_guard<std::mutex> lock(initMutex);
freqDetailList.clear();
if (!hasInit) {
CpuFreqManager::GetCurFreqDetail();
} else {
CpuFreqManager::GetInstance()->CalFreqDetail();
}
return freqDetailList;
}