From b2a9e9cd70506b970c167be90221dbdf657c8684 Mon Sep 17 00:00:00 2001 From: Junyi Ye <294572668@qq.com> Date: Sun, 7 Apr 2024 17:16:46 +0800 Subject: [PATCH 01/14] pfm --- pmu/pfm/CMakeLists.txt | 15 ++ pmu/pfm/core.h | 26 ++++ pmu/pfm/pfm.cpp | 320 +++++++++++++++++++++++++++++++++++++++++ pmu/pfm/pfm.h | 29 ++++ pmu/pfm/pfm_event.h | 55 +++++++ pmu/pfm/trace.cpp | 40 ++++++ pmu/pfm/uncore.h | 28 ++++ 7 files changed, 513 insertions(+) create mode 100644 pmu/pfm/CMakeLists.txt create mode 100644 pmu/pfm/core.h create mode 100644 pmu/pfm/pfm.cpp create mode 100644 pmu/pfm/pfm.h create mode 100644 pmu/pfm/pfm_event.h create mode 100644 pmu/pfm/trace.cpp create mode 100644 pmu/pfm/uncore.h diff --git a/pmu/pfm/CMakeLists.txt b/pmu/pfm/CMakeLists.txt new file mode 100644 index 0000000..6a84de0 --- /dev/null +++ b/pmu/pfm/CMakeLists.txt @@ -0,0 +1,15 @@ +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif (POLICY CMP0048) +project(libpfm) +cmake_minimum_required (VERSION 3.12.0) + +set(PMU_FILE_DIR ${PROJECT_TOP_DIR}/pmu) +set(PFM_FILE_DIR ${PROJECT_TOP_DIR}/pmu/pfm) +file(GLOB PFM_SRC ${PFM_FILE_DIR}/*c ${PFM_FILE_DIR}/*cpp) +include_directories(${PROJECT_TOP_DIR}/include) +include_directories(${PMU_FILE_DIR}/) +include_directories(PFM_FILE_DIR) + +ADD_LIBRARY(pfm SHARED ${PFM_SRC}) +target_compile_options(pfm PRIVATE -fPIC) \ No newline at end of file diff --git a/pmu/pfm/core.h b/pmu/pfm/core.h new file mode 100644 index 0000000..0afe1c7 --- /dev/null +++ b/pmu/pfm/core.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: core event map declaration + ******************************************************************************/ +#ifndef CORE_H +#define CORE_H +#include +#include "pfm_event.h" +#include "pfm_name.h" + +namespace KUNPENG_PMU { + extern const KUNPENG_PMU::CORE_EVT_MAP CORE_EVENT_MAP; +} + +#endif + diff --git a/pmu/pfm/pfm.cpp b/pmu/pfm/pfm.cpp new file mode 100644 index 0000000..07f9ec1 --- /dev/null +++ b/pmu/pfm/pfm.cpp @@ -0,0 +1,320 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: event configuration query + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "trace.h" +#include "securec.h" +#include "common.h" +#include "cpu_map.h" +#include "pfm_event.h" +#include "pmu_event.h" +#include "pmu.h" +#include "pcerr.h" +#include "pfm.h" + +#include "uncore.h" +#include "core.h" + +using namespace std; +using namespace pcerr; +using namespace KUNPENG_PMU; + +static constexpr int MAX_STRING_LEN = 2048; +static CHIP_TYPE g_chipType = UNDEFINED_TYPE; + +static struct PmuEvt* ConstructPmuEvtFromCore(KUNPENG_PMU::CoreConfig config, int collectType) +{ + struct PmuEvt* pmuEvtPtr = new PmuEvt; + pmuEvtPtr->config = config.config; + pmuEvtPtr->name = config.eventName; + pmuEvtPtr->type = config.type; + pmuEvtPtr->collectType = collectType; + return std::move(pmuEvtPtr); +} + +static struct PmuEvt* ConstructPmuEvtFromUncore(KUNPENG_PMU::UncoreConfig config, int collectType) +{ + struct PmuEvt* pmuEvtPtr = new PmuEvt; + pmuEvtPtr->config = config.config; + pmuEvtPtr->name = config.eventName; + pmuEvtPtr->type = config.type; + pmuEvtPtr->pmuType = config.pmuType; + pmuEvtPtr->collectType = collectType; + return std::move(pmuEvtPtr); +} + +static struct PmuEvt* GetCoreEvent(const char* pmuName, int collectType) +{ + return KUNPENG_PMU::CORE_EVENT_MAP.at(g_chipType).find(pmuName) != + KUNPENG_PMU::CORE_EVENT_MAP.at(g_chipType).end() + ? ConstructPmuEvtFromCore( + KUNPENG_PMU::CORE_EVENT_MAP.at(g_chipType).at(pmuName), collectType) + : nullptr; +} + +static struct PmuEvt* GetUncoreDDRCEvent(const char* pmuName, int collectType) +{ + return KUNPENG_PMU::DDRC_EVENT_MAP.at(g_chipType).find(pmuName) != + KUNPENG_PMU::DDRC_EVENT_MAP.at(g_chipType).end() + ? ConstructPmuEvtFromUncore(KUNPENG_PMU::DDRC_EVENT_MAP.at(g_chipType).at(pmuName), collectType) + : nullptr; +} + +static struct PmuEvt* GetUncoreHHAEvent(const char* pmuName, int collectType) +{ + return KUNPENG_PMU::HHA_EVENT_MAP.at(g_chipType).find(pmuName) != + KUNPENG_PMU::HHA_EVENT_MAP.at(g_chipType).end() + ? ConstructPmuEvtFromUncore( + KUNPENG_PMU::HHA_EVENT_MAP.at(g_chipType).at(pmuName), collectType) + : nullptr; +} + +static struct PmuEvt* GetUncoreLLCEvent(const char* pmuName, int collectType) +{ + return KUNPENG_PMU::L3C_EVENT_MAP.at(g_chipType).find(pmuName) != + KUNPENG_PMU::L3C_EVENT_MAP.at(g_chipType).end() + ? ConstructPmuEvtFromUncore( + KUNPENG_PMU::L3C_EVENT_MAP.at(g_chipType).at(pmuName), collectType) + : nullptr; +} + +static struct PmuEvt* GetKernelTraceEvent(const char* pmuName, int collectType) +{ + int64_t config = GetTraceEventID(pmuName); + if (config == -1) { + return nullptr; + } + auto* pmuEvtPtr = new PmuEvt; + pmuEvtPtr->config = config; + pmuEvtPtr->name = pmuName; + pmuEvtPtr->type = PERF_TYPE_TRACEPOINT; + pmuEvtPtr->collectType = collectType; + return pmuEvtPtr; +} + +static int GetSpeType(void) +{ + constexpr char* speTypePath = "/sys/devices/arm_spe_0/type"; + FILE *fp = fopen(speTypePath, "r"); + int type; + + if (!fp) { + return -1; + } + + if (fscanf_s(fp, "%d", &type, sizeof(int)) != 1) { + if (fclose(fp) == EOF) { + return -1; + } + return -1; + } + + if (fclose(fp) == EOF) { + return -1; + } + return type; +} + +using EvtRetriever = std::function; + +static const std::unordered_map EvtMap{ + {KUNPENG_PMU::CORE_TYPE, GetCoreEvent}, + {KUNPENG_PMU::HHA_TYPE, GetUncoreHHAEvent}, + {KUNPENG_PMU::L3C_TYPE, GetUncoreLLCEvent}, + {KUNPENG_PMU::DDRC_TYPE, GetUncoreDDRCEvent}, + {KUNPENG_PMU::TRACE_TYPE, GetKernelTraceEvent}, +}; + +static int GetEventType(const char *pmuName, string &evtName, string &devName) +{ + auto coreMap = CORE_EVENT_MAP.at(g_chipType); + auto findCoreEvent = coreMap.find(pmuName); + if (findCoreEvent != coreMap.end()) { + evtName = pmuName; + return CORE_TYPE; + } + std::string strName(pmuName); + + // Kernel trace point event name like 'block:block_bio_complete' + if (strName.find(':') != string::npos) { + evtName = pmuName; + return TRACE_TYPE; + } + + // Parse uncore event name like 'hisi_sccl3_ddrc0/flux_rd/' + auto findSlash = strName.find('/'); + if (findSlash == string::npos) { + return -1; + } + devName = strName.substr(0, findSlash); + findSlash = strName.find('/', findSlash); + if (findSlash == string::npos) { + return -1; + } + evtName = strName.substr(devName.size() + 1, strName.size() - 1 - (devName.size() + 1)); + auto ddrMap = DDRC_EVENT_MAP.at(g_chipType); + auto findDDrEvent = ddrMap.find(evtName); + if (findDDrEvent != ddrMap.end()) { + return DDRC_TYPE; + } + + return -1; +} + +static int GetDeviceType(const string &devName) +{ + string typePath = "/sys/devices/" + 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 int GetCpuMask(const string &devName) +{ + string maskPath = "/sys/devices/" + devName + "/cpumask"; + std::string realPath = GetRealPath(maskPath); + if (!IsValidPath(realPath)) { + return -1; + } + ifstream maskIn(realPath); + if (!maskIn.is_open()) { + return -1; + } + // Cpumask is a comma-separated list of integers, + // but now make it simple for ddrc event. + string maskStr; + maskIn >> maskStr; + + return stoi(maskStr); +} + +static bool GetEvtConfig(const string &devName, const char* pmuName, const string &evtName, __u64 &config) +{ + string evtPath = "/sys/devices/" + devName + "/events/" + evtName; + std::string realPath = GetRealPath(evtPath); + if (!IsValidPath(realPath)) { + return false; + } + ifstream evtIn(realPath); + if (!evtIn.is_open()) { + return false; + } + string configStr; + evtIn >> configStr; + auto findEq = configStr.find("="); + if (findEq == string::npos) { + return false; + } + auto subStr = configStr.substr(findEq + 1, configStr.size() - findEq); + std::istringstream iss(subStr); + iss >> std::hex >> config; + + return true; +} + +static int FillUncoreFields(const string &devName, const char* pmuName, const string &evtName, PmuEvt *evt) +{ + int devType = GetDeviceType(devName); + if (devType == -1) { + return UNKNOWN_ERROR; + } + evt->type = devType; + int cpuMask = GetCpuMask(devName); + if (cpuMask == -1) { + return UNKNOWN_ERROR; + } + if (!GetEvtConfig(devName, pmuName, evtName, evt->config)) { + return LIBPERF_ERR_INVALID_EVENT; + } + + evt->cpumask = cpuMask; + evt->name = pmuName; + return SUCCESS; +} + +struct PmuEvt* PfmGetPmuEvent(const char* pmuName, int collectType) +{ + if (pmuName == nullptr) { + auto* evt = new PmuEvt {0}; + evt->collectType = collectType; + return evt; + } + string evtName; + string devName; + g_chipType = GetCpuType(); + if (g_chipType == UNDEFINED_TYPE) { + return nullptr; + } + auto type = GetEventType(pmuName, evtName, devName); + if (type == -1) { + return nullptr; + } + struct PmuEvt* evt = (EvtMap.find(type) != EvtMap.end()) ? + EvtMap.at(type)(evtName.c_str(), collectType) : nullptr; + if (evt == nullptr) { + return evt; + } + if (!devName.empty()) { + // Fill fields for uncore devices. + auto err = FillUncoreFields(devName, pmuName, evtName, evt); + if (err != SUCCESS) { + return nullptr; + } + } else if (evt != nullptr) { + evt->cpumask = -1; + } + return evt; +} + +struct PmuEvt* PfmGetSpeEvent( + unsigned long dataFilter, unsigned long eventFilter, unsigned long minLatency, int collectType) +{ + PmuEvt* evt = new PmuEvt{0}; + evt->collectType = collectType; + int type = GetSpeType(); + if (type == -1) { + return nullptr; + } + evt->type = static_cast(type); + evt->config = dataFilter; + evt->config1 = eventFilter; + evt->config2 = minLatency; + evt->cpumask = -1; + + return evt; +} + +void PmuEvtFree(PmuEvt *evt) +{ + if (evt != nullptr) { + delete evt; + } +} \ No newline at end of file diff --git a/pmu/pfm/pfm.h b/pmu/pfm/pfm.h new file mode 100644 index 0000000..78bd679 --- /dev/null +++ b/pmu/pfm/pfm.h @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: event query interface, release interface + ******************************************************************************/ +#ifndef PFM_H +#define PFM_H +#ifdef __cplusplus +extern "C" { +#endif +struct PmuEvt* PfmGetPmuEvent(const char* pmuName, int collectType); +struct PmuEvt* PfmGetSpeEvent( + unsigned long dataFilter, unsigned long eventFilter, unsigned long minLatency, int collectType); +void PmuEvtFree(PmuEvt *evt); +#ifdef __cplusplus +} +#endif + + +#endif diff --git a/pmu/pfm/pfm_event.h b/pmu/pfm/pfm_event.h new file mode 100644 index 0000000..0112089 --- /dev/null +++ b/pmu/pfm/pfm_event.h @@ -0,0 +1,55 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: event type definitions, declarations, prototypes + ******************************************************************************/ +#ifndef PFM_EVENT_H +#define PFM_EVENT_H +#include +#include +#include +#include "cpu_map.h" +#include "pmu.h" + +namespace KUNPENG_PMU { + enum PMU_TYPE { + CORE_TYPE, + DDRC_TYPE, + HHA_TYPE, + L3C_TYPE, + PCIE_TYPE, + TRACE_TYPE, + }; + + struct UncoreConfig { + __u64 type; + __u64 config; + __u64 config1; + __u64 config2; + enum PMU_TYPE pmuType; + int cpuMask; + const std::string eventName; + const std::string desc; + }; + + struct CoreConfig { + __u64 type; + __u64 config; + const std::string eventName; + const std::string desc; + }; + using UNCORE_EVT_MAP = + std::unordered_map&>; + using CORE_EVT_MAP = + std::unordered_map&>; +} // namespace KUNPENG_PMU +#endif \ No newline at end of file diff --git a/pmu/pfm/trace.cpp b/pmu/pfm/trace.cpp new file mode 100644 index 0000000..b13ce19 --- /dev/null +++ b/pmu/pfm/trace.cpp @@ -0,0 +1,40 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: trace point event configuration query + ******************************************************************************/ +#include +#include "common.h" +#include "trace.h" + +using namespace std; + +int64_t GetTraceEventID(const std::string &name) +{ + size_t colon = name.find(':'); + string systemName = name.substr(0, colon); + string eventName = name.substr(colon + 1); + + string eventPath = "/sys/kernel/tracing/events/" + systemName + "/" + eventName + "/id"; + string realPath = GetRealPath(eventPath); + if (!IsValidPath(realPath)) { + return -1; + } + ifstream typeIn(realPath); + if (!typeIn.is_open()) { + return -1; + } + string typeStr; + typeIn >> typeStr; + + return stoi(typeStr); +} diff --git a/pmu/pfm/uncore.h b/pmu/pfm/uncore.h new file mode 100644 index 0000000..002e8d5 --- /dev/null +++ b/pmu/pfm/uncore.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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 map declaration + ******************************************************************************/ +#ifndef UNCORE_H +#define UNCORE_H +#include +#include "pfm_event.h" +#include "pfm_name.h" + +namespace KUNPENG_PMU { + extern const KUNPENG_PMU::UNCORE_EVT_MAP L3C_EVENT_MAP; + extern const KUNPENG_PMU::UNCORE_EVT_MAP HHA_EVENT_MAP; + extern const KUNPENG_PMU::UNCORE_EVT_MAP DDRC_EVENT_MAP; + extern const KUNPENG_PMU::UNCORE_EVT_MAP PCIE_EVENT_MAP; +} // namespace KUNPENG_PMU + +#endif \ No newline at end of file -- Gitee From aaf49466f4625d446352f807af89b8249e18221a Mon Sep 17 00:00:00 2001 From: Galaxy Date: Sat, 6 Apr 2024 17:47:03 -0700 Subject: [PATCH 02/14] First commit for headers and sampling. This commit includes three parts: - headers of oeAware-collector - parsing data from ring buffer for pmu sampling - parsing data from ring buffer for counting Headers, pmu.h and pcerrc.h are interface of oeAware-collector. pmu.h includes basic operations and data structure: - PmuOpen - PmuCollect - PmuRead - PmuClose - struct PmuData pcerrc.h includes error codes of oeAware-collector. Refer to README for detailed information. sampler.cpp and sample_process.cpp provide function of parsing data from ring buffer for pmu sampling. They provide low-level api for opening perf event and reading packets from buffer. For now, only non-overwrite mode is used and only some kinds of packet type are in attention: - PERF_RECORD_SAMPLE - PERF_RECORD_MMAP - PERF_RECORD_MMAP2 - PERF_RECORD_FORK perf_counter.cpp provide function of parsing data from ring buffer for counting.It provides low-level api for opening event and reading pmu count from buffer. The field 'count' of struct PmuData is filled in function Read(). --- include/pcerrc.h | 99 ++++++++++++++++++ include/pmu.h | 162 +++++++++++++++++++++++++++++ pmu/perf_counter.cpp | 114 +++++++++++++++++++++ pmu/perf_counter.h | 43 ++++++++ pmu/sample_process.cpp | 163 +++++++++++++++++++++++++++++ pmu/sample_process.h | 32 ++++++ pmu/sampler.cpp | 226 +++++++++++++++++++++++++++++++++++++++++ pmu/sampler.h | 64 ++++++++++++ 8 files changed, 903 insertions(+) create mode 100644 include/pcerrc.h create mode 100644 include/pmu.h create mode 100644 pmu/perf_counter.cpp create mode 100644 pmu/perf_counter.h create mode 100644 pmu/sample_process.cpp create mode 100644 pmu/sample_process.h create mode 100644 pmu/sampler.cpp create mode 100644 pmu/sampler.h diff --git a/include/pcerrc.h b/include/pcerrc.h new file mode 100644 index 0000000..dc0fc6d --- /dev/null +++ b/include/pcerrc.h @@ -0,0 +1,99 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: global error codes of perf.so + ******************************************************************************/ +#ifndef PCERRC_H +#define PCERRC_H +#ifdef __cplusplus +extern "C" { +#endif +// default code +#define SUCCESS 0 +#define COMMON_ERR_NOMEM 1 // not enough memory + +// libsym 100-1000 +#define LIBSYM_ERR_BASE 100 +#define LIBSYM_ERR_KALLSYMS_INVALID LIBSYM_ERR_BASE +#define LIBSYM_ERR_DWARF_FORMAT_FAILED 101 +#define LIBSYM_ERR_ELFIN_FOMAT_FAILED 102 +#define LIBSYM_ERR_OPEN_FILE_FAILED 103 +#define LIBSYM_ERR_NOT_FIND_PID 104 +#define LIBSYM_ERR_MAP_ADDR_MODULE_FAILED 105 +#define LIBSYM_ERR_MAP_KERNAL_ADDR_FAILED 106 +#define LIBSYM_ERR_PARAM_PID_INVALID 107 +#define LIBSYM_ERR_STRCPY_OPERATE_FAILED 108 +#define LIBSYM_ERR_SNPRINF_OPERATE_FAILED 109 +#define LIBSYM_ERR_MAP_CODE_KERNEL_NOT_SUPPORT 110 +#define LIBSYM_ERR_MAP_CODE_FIND_ELF_FAILED 111 +#define LIBSYM_ERR_CMD_OPERATE_FAILED 112 +#define LIBSYM_ERR_FILE_NOT_RGE 113 +#define LIBSYM_ERR_START_SMALLER_END 114 +#define LIBSYM_ERR_STOUL_OPERATE_FAILED 115 +#define LIBSYM_ERR_FILE_INVALID 116 +// libperf 1000-3000 +#define LIBPERF_ERR_NO_AVAIL_PD 1000 +#define LIBPERF_ERR_CHIP_TYPE_INVALID 1001 +#define LIBPERF_ERR_FAIL_LISTEN_PROC 1002 +#define LIBPERF_ERR_INVALID_CPULIST 1003 +#define LIBPERF_ERR_INVALID_PIDLIST 1004 +#define LIBPERF_ERR_INVALID_EVTLIST 1005 +#define LIBPERF_ERR_INVALID_PD 1006 +#define LIBPERF_ERR_INVALID_EVENT 1007 +#define LIBPERF_ERR_SPE_UNAVAIL 1008 +#define LIBPERF_ERR_FAIL_GET_CPU 1009 +#define LIBPERF_ERR_FAIL_GET_PROC 1010 +#define LIBPERF_ERR_NO_PERMISSION 1011 +#define LIBPERF_ERR_DEVICE_BUSY 1012 +#define LIBPERF_ERR_DEVICE_INVAL 1013 +#define LIBPERF_ERR_FAIL_MMAP 1014 +#define LIBPERF_ERR_FAIL_RESOLVE_MODULE 1015 +#define LIBPERF_ERR_KERNEL_NOT_SUPPORT 1016 +#define LIBPERF_ERR_INVALID_METRIC_TYPE 1017 +#define LIBPERF_ERR_INVALID_PID 1018 +#define LIBPERF_ERR_INVALID_TASK_TYPE 1019 +#define LIBPERF_ERR_INVALID_TIME 1020 +#define LIBPERF_ERR_NO_PROC 1021 +#define LIBPERF_ERR_TOO_MANY_FD 1022 +#define LIBPERF_ERR_RAISE_FD 1023 +// libebpf 3000-4000 + +// numafast 4001-5000 +#define NUMAFAST_ERR_SPE_UNAVAILABLE 4001 +#define NUMAFAST_ERR_SET_RES_LIMIT 4002 +#define NUMAFAST_ERR_FILE_OPERATION 4003 +#define NUMAFAST_ERR_INIT_RES 4004 +#define NUMAFAST_ERR_INIT_NODE_COLLECTION 4005 +#define NUMAFAST_ERR_INIT_ANALYZE 4006 +#define NUMAFAST_ERR_INIT_UNCORE_EVENT 4007 +#define NUMAFAST_ERR_ENABLE_UNCORE_EVENT 4008 +#define NUMAFAST_ERR_START_PROFILING 4009 +#define NUMAFAST_ERR_ANALYZE 4010 +#define NUMAFAST_ERR_HELP 4011 +#define NUMAFAST_ERR_BUFFER_INIT 4012 +#define NUMAFAST_ERR_PARAM 4999 + +#define UNKNOWN_ERROR 9999 + +/** +* @brief Obtaining error codes +*/ +int Perrorno(); + +/** +* @brief Obtaining Error Information +*/ +const char* Perror(); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/include/pmu.h b/include/pmu.h new file mode 100644 index 0000000..2e22b87 --- /dev/null +++ b/include/pmu.h @@ -0,0 +1,162 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: declarations and definitions of interfaces and data structures exposed by perf.so + ******************************************************************************/ +#ifndef PMU_DATA_STRUCT_H +#define PMU_DATA_STRUCT_H +#include +#include +#ifdef __cplusplus +extern "C" { +#endif +#pragma GCC visibility push(default) + +enum PmuTaskType { + COUNTING = 0, // pmu counting task + SAMPLING = 1, // pmu sampling task + SPE_SAMPLING = 2, // spe sampling task + MAX_TASK_TYPE +}; + +enum AggregateType { + PER_SYSTEM, + PER_CORE, + PER_NUMA, + PER_SOCKET, + PER_THREAD, +}; + +enum SpeFilter { + SPE_FILTER_NONE = 0, + TS_ENABLE = 1UL << 0, // enable timestamping with value of generic timer + PA_ENABLE = 1UL << 1, // collect physical address (as well as VA) of loads/stores + PCT_ENABLE = 1UL << 2, // collect physical timestamp instead of virtual timestamp + JITTER = 1UL << 16, // use jitter to avoid resonance when sampling + BRANCH_FILTER = 1UL << 32, // collect branches only + LOAD_FILTER = 1UL << 33, // collect loads only + STORE_FILTER = 1UL << 34, // collect stores only + SPE_DATA_ALL = TS_ENABLE | PA_ENABLE | PCT_ENABLE | JITTER | BRANCH_FILTER | LOAD_FILTER | STORE_FILTER +}; + +enum SpeEventFilter { + SPE_EVENT_NONE = 0, + SPE_EVENT_RETIRED = 0x2, // instruction retired + SPE_EVENT_L1DMISS = 0x8, // L1D refill + SPE_EVENT_TLB_WALK = 0x20, // TLB refill + SPE_EVENT_MISPREDICTED = 0x80, // mispredict +}; + +struct PmuAttr { + char** evtList; // event list + unsigned numEvt; // length of event list + int* pidList; // pid list + unsigned numPid; // length of pid list + int* cpuList; // cpu id list + unsigned numCpu; // length of cpu id list + + union { + unsigned period; // sample period + unsigned freq; // sample frequency + }; + unsigned useFreq : 1; + + // SPE related fields. + enum SpeFilter dataFilter; // spe data filter + enum SpeEventFilter evFilter; // spe event filter + unsigned long minLatency; // collect only samples with latency or higher +}; + +struct PmuDataExt { + unsigned long pa; // physical address + unsigned long va; // virtual address + unsigned long event; // event id +}; + +struct PmuData { + struct Stack* stack; // call stack + const char *evt; // event name + int64_t ts; // time stamp + pid_t pis; // process id + int tid; // thread id + unsigned cpu; // cpu id + struct CpuTopology *cpuTopo; // cpu topology + const char *comm; // process command + + union { + uint64_t count; // event count. Only available for Counting. + struct PmuDataExt *ext; // extension. Only available for Spe. + }; +}; + +/** + * @brief + * Initialize the collection target. + * @param collectType collection typr. + * @param evtList array of event IDs + * @param numEvt length of evtList. + * @param pidList list of PIDs to be collected. Information about subprocess and subthreads of PIDs is collected. If + * the value is NULL, all process/threads are collected + * @param numPid length of pidList. + * @param cpuList CPU ID list. If the value is NULL, all CPUs are collected. + * @param numCpu cpuList length. + * @return int + */ +int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr); + +/** + * @brief + * Collect milliseconds. If is equal to - 1 and the PID list is not empty, the collection + * is performed until all processes are compete. + * @param milliseconds + * @return int + */ +int PmuCollect(int pd, int milliseconds); + +/** + * @brief + * Similar to , and accepts multiple pds. + * @param milliseconds + * @return int + */ +int PmuCollectV(int *pd, int milliseconds); + +/** + * @brief stop a sampling task in asynchronous mode + * @param pd pmu descriptor. + */ +int PmuStop(int pd); + +/** + * @brief + * Collect data. If the value is NULL and the error code is 0, no data is available in the current collection time. If + * the value is NULL and the error code is not 0, an error occurs in the collection process and data cannot be read. + * @param struct PmuData* + */ +int PmuRead(int pd, struct PmuData** pmuData); + +/** + * @brief Close all the file descriptor opened during collecting process + */ +int PmuClose(int pd); + +/** + * @brief Free PmuData pointer. + * @param pmuData + */ +void PmuDataFree(struct PmuData** pmuData); + +#pragma GCC visibility pop +#ifdef __cplusplus +} +#endif +#endif diff --git a/pmu/perf_counter.cpp b/pmu/perf_counter.cpp new file mode 100644 index 0000000..cb04a5e --- /dev/null +++ b/pmu/perf_counter.cpp @@ -0,0 +1,114 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: implementations for reading performance counters and initializing counting logic in + * the KUNPENG_PMU namespace. + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "pmu.h" +#include "linked_list.h" +#include "pmu_event.h" +#include "pcerr.h" +#include "log.h" +#include "perf_counter.h" + +using namespace std; + +static constexpr int MAX_ATTR_SIZE = 120; +/** + * Read pmu counter and deal with pmu multiplexing + * Right now we do not implement grouping logic, thus we ignore the + * PERF_FORMAT_ID section for now + */ +int KUNPENG_PMU::PerfCounter::Read(vector &data, std::vector &sampleIps) +{ + struct ReadFormat perfCountValue; + + /** + * If some how the file descriptor is less than 0, + * we make the count to be 0 and return + */ + if (__glibc_unlikely(this->fd < 0)) { + this->count = 0; + return UNKNOWN_ERROR; + } + read(this->fd, &perfCountValue, sizeof(perfCountValue)); + + /** + * In case of multiplexing, we follow the linux documentation for calculating the estimated + * counting value (https://perf.wiki.kernel.org/index.php/Tutorial) + */ + + /** + * For now we assume PMU register was reset before each collection, so we assign the counting value to the + * count section in data. We will implement the aggregating logic soon + */ + this->count = perfCountValue.value; + data.emplace_back(PmuData{0}); + auto& current = data.back(); + current.count = this->count; + current.cpu = static_cast(this->cpu); + current.tid = this->pid; + auto findProc = procMap.find(current.tid); + if (findProc != procMap.end()) { + current.pid = findProc->second->pid; + } + return SUCCESS; +} + +/** + * Initialize counting + */ +int KUNPENG_PMU::PerfCounter::Init() +{ + return this->MapPerfAttr(); +} + +int KUNPENG_PMU::PerfCounter::MapPerfAttr() +{ + /** + * For now, we only implemented the logic for CORE type events. Support for UNCORE PMU events will be + * added soon + */ + struct perf_event_attr attr; + if (memset_s(&attr, MAX_ATTR_SIZE, 0, sizeof(attr)) != EOK) { + return UNKNOWN_ERROR; + } + attr.size = sizeof(struct perf_event_attr); + attr.type = this->evt->type; + attr.config = this->evt->config; + + /** + * We want to set the disabled and inherit bit to collect child processes + */ + attr.disabled = 1; + attr.inherit = 1; + + /** + * For now we set the format id bit to implement grouping logic in the future + */ + attr.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID; + this->fd = PerfEventOpen(&attr, this->pid, this->cpu, -1, 0); + DBG_PRINT("type: %d cpu: %d config: %X\n", attr.type, cpu, attr.config); + if (__glibc_unlikely(this->fd < 0)) { + return MapErrno(errno); + } + return SUCCESS; +} \ No newline at end of file diff --git a/pmu/perf_counter.h b/pmu/perf_counter.h new file mode 100644 index 0000000..19628fc --- /dev/null +++ b/pmu/perf_counter.h @@ -0,0 +1,43 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: declaration of class PerfCounter that inherits from PerfEvt and provides implementations + * for initializing, reading, and mapping performance counter attributes in the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_COUNTER_H +#define PMU_COUNTER_H + +#include +#include +#include +#include "evt.h" +#include "pmu_event.h" + +struct ReadFormat { + __u64 value; /* The value of the event */ + __u64 timeEnabled; /* if PERF_FORMAT_TOTAL_timeEnabled */ + __u64 timeRunning; /* if PERF_FORMAT_TOTAL_timeRunning */ + __u64 id; /* if PERF_FORMAT_ID */ +}; + +namespace KUNPENG_PMU { + class PerfCounter : public PerfEvt { + public: + using PerfEvt::PerfEvt; + ~PerfCounter() + {} + int Init() override; + int Read(std::vector &data, std::vector &sampleIps) override; + int MapPerfAttr() override; + }; +} // namespace KUNPENG_PMU +#endif diff --git a/pmu/sample_process.cpp b/pmu/sample_process.cpp new file mode 100644 index 0000000..e54c5a7 --- /dev/null +++ b/pmu/sample_process.cpp @@ -0,0 +1,163 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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 reading and managing data from a ring buffer in the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include +#include "securec.h" +#include "pcerrc.h" +#include "evt.h" +#include "sample_process.h" + +#define PAGE_SIZE (sysconf(_SC_PAGESIZE)) +#define MB() asm volatile("dmb ish" ::: "memory") +static constexpr int MAX_DATA_SIZE = 8192; +#define PerfRingbufferSmpStoreRelease(p, v) \ + ({ \ + union { \ + typeof(*p) val; \ + char charHead[1]; \ + } pointerUnion = {.val = (v)}; \ + asm volatile("stlr %1, %0" : "=Q"(*p) : "r"(*(__u64 *)pointerUnion.charHead) : "memory"); \ + }) + +void KUNPENG_PMU::PerfMmapConsume(PerfMmap &map) +{ + __u64 prev = map.prev; + struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base; + PerfRingbufferSmpStoreRelease(&base->data_tail, prev); +} + +void KUNPENG_PMU::PerfMmapReadDone(PerfMmap &map) +{ + struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base; + map.prev = ReadOnce(&base->data_head); +} + +int KUNPENG_PMU::MmapInit(PerfMmap &sampleMmap) +{ + struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)sampleMmap.base; + __u64 head = ReadOnce(&base->data_head); + __u64 prev = sampleMmap.prev; + unsigned long size; + + sampleMmap.start = sampleMmap.overwrite ? head : prev; + sampleMmap.end = sampleMmap.overwrite ? prev : head; + + if (__glibc_unlikely((sampleMmap.end - sampleMmap.start) < sampleMmap.flush)) { + return -EAGAIN; + } + + size = sampleMmap.end - sampleMmap.start; + if (size > (unsigned long)(sampleMmap.mask) + 1) { + if (!sampleMmap.overwrite) { + sampleMmap.prev = head; + PerfMmapConsume(sampleMmap); + } + } + return 0; +} + +void CopyDataInWhileLoop(KUNPENG_PMU::PerfMmap& map, __u64 offset, unsigned char* data, __u64 len) +{ + /* + * Here, as long as we still have data inside the mmap, we continue to wrap around + * the head and tail pointer to a point that + */ + char *tmpDataPtr = map.copiedEvent; + __u64 copiedData = 0; + while (len) { + __u64 restSize = offset & map.mask; + copiedData = map.mask + 1 - restSize < len ? map.mask + 1 - restSize : len; + + if (memcpy_s(tmpDataPtr, MAX_DATA_SIZE, &data[restSize], copiedData) != EOK) { + perror("failed to memcpy_s"); + } + + offset += copiedData; + tmpDataPtr += copiedData; + len -= copiedData; + } +} + +static inline union KUNPENG_PMU::PerfEvent *PerfMmapRead(KUNPENG_PMU::PerfMmap &map, __u64 *startPointer, __u64 end) +{ + /* + * Logic for reading ringbuffer + */ + unsigned char *data = (unsigned char *)map.base + PAGE_SIZE; + union KUNPENG_PMU::PerfEvent *event = nullptr; + __u64 diff = end - *startPointer; + if (diff >= sizeof(event->header)) { + size_t size; + + event = (union KUNPENG_PMU::PerfEvent *)&data[*startPointer & map.mask]; + size = event->header.size; + + if (__glibc_unlikely(size < sizeof(event->header) || diff < size)) { + return nullptr; + } + + size_t event_size = sizeof(*event); + if ((*startPointer & map.mask) + size != ((*startPointer + size) & map.mask)) { + __u64 offset = *startPointer; + __u64 len = event_size < size ? event_size : size; + CopyDataInWhileLoop(map, offset, data, len); + event = (union KUNPENG_PMU::PerfEvent *)map.copiedEvent; + } + + *startPointer += size; + } + + return event; +} + +union KUNPENG_PMU::PerfEvent *KUNPENG_PMU::ReadEvent(PerfMmap &map) +{ + if (!map.overwrite) { + struct perf_event_mmap_page *base = (struct perf_event_mmap_page *)map.base; + map.end = ReadOnce(&base->data_head); + } + + union KUNPENG_PMU::PerfEvent *event = PerfMmapRead(map, &map.start, map.end); + + if (!map.overwrite) { + map.prev = map.start; + } + + return event; +} + +int KUNPENG_PMU::RingbufferReadInit(PerfMmap &map) +{ + __u64 head = ReadOnce(&map.base->data_head); + __u64 prev = map.prev; + unsigned long size; + + map.start = map.overwrite ? head : prev; + map.end = map.overwrite ? prev : head; + + if (__glibc_unlikely((map.end - map.start) < map.flush)) { + return UNKNOWN_ERROR; + } + + size = map.end - map.start; + if (size > (unsigned long)(map.mask) + 1) { + if (!map.overwrite) { + map.prev = head; + PerfMmapConsume(map); + } + } + return SUCCESS; +} diff --git a/pmu/sample_process.h b/pmu/sample_process.h new file mode 100644 index 0000000..0878845 --- /dev/null +++ b/pmu/sample_process.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: definition of functions for handling performance event sampling processes in + * the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_SAMPLE_PROCESS_H +#define PMU_SAMPLE_PROCESS_H +#include +#include "pmu_event.h" + +namespace KUNPENG_PMU { + + int MmapInit(PerfMmap& sampleMmap); + union PerfEvent* ReadEvent(PerfMmap& map); + int RingbufferReadInit(PerfMmap& map); + void PerfMmapConsume(PerfMmap& map); + void PerfMmapReadDone(PerfMmap& map); + int CreateRingbuffer(PerfMmap& map, int mask, int prot, int fd); + +} // namespace KUNPENG_PMU + +#endif diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp new file mode 100644 index 0000000..b323a3e --- /dev/null +++ b/pmu/sampler.cpp @@ -0,0 +1,226 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: implementations for sampling and processing performance data using ring buffers in + * the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "linked_list.h" +#include "symbol_resolve.h" +#include "util_time.h" +#include "sample_process.h" +#include "pcerrc.h" +#include "process_map.h" +#include "log.h" +#include "sampler.h" + +using namespace std; + +static constexpr int PAGE_SIZE = 1024; +static constexpr int MAX_ATTR_SIZE = 120; +static constexpr int SAMPLE_FREQ = 1000; +int KUNPENG_PMU::PerfSampler::pages = 128; + +template +static inline bool IsPowerOfTwo(T x) +{ + return (x) != 0 && (((x) & ((x) - 1)) == 0); +} + +int KUNPENG_PMU::PerfSampler::MapPerfAttr() +{ + struct perf_event_attr attr; + if (memset_s(&attr, MAX_ATTR_SIZE, 0, sizeof(attr)) != EOK) { + return UNKNOWN_ERROR; + } + attr.type = this->evt->type; + attr.config = this->evt->config; + attr.size = sizeof(struct perf_event_attr); + attr.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_CALLCHAIN | PERF_SAMPLE_ID | + PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_IDENTIFIER; + attr.freq = this->evt->useFreq; + attr.sample_period = this->evt->period; + attr.read_format = PERF_FORMAT_ID; + attr.pinned = 1; + attr.disabled = 1; + attr.inherit = 1; + attr.mmap = 1; + attr.comm = 1; + attr.mmap2 = 1; + attr.task = 1; + attr.sample_id_all = 1; + attr.exclude_guest = 1; + + this->fd = PerfEventOpen(&attr, this->pid, this->cpu, -1, 0); + DBG_PRINT("pid: %d type: %d cpu: %d config: %X\n", this->pid, attr.type, cpu, attr.config); + if (__glibc_unlikely(this->fd < 0)) { + return MapErrno(errno); + } + return SUCCESS; +} + +union KUNPENG_PMU::PerfEvent *KUNPENG_PMU::PerfSampler::SampleReadEvent() +{ + return ReadEvent(*this->sampleMmap); +} + +bool KUNPENG_PMU::PerfSampler::Close() +{ + return PerfEvt::Close(); +} + +int KUNPENG_PMU::PerfSampler::Mmap() +{ + int mmapLen = (this->pages + 1) * PAGE_SIZE; + auto mask = mmapLen - PAGE_SIZE - 1; + if (mask < 0) { + return UNKNOWN_ERROR; + } + + this->sampleMmap->prev = 0; + this->sampleMmap->mask = static_cast<__u64>(mask); + void *currentMap = + mmap(NULL, this->sampleMmap->mask + 1 + PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (__glibc_unlikely(currentMap == MAP_FAILED)) { + this->sampleMmap->base = nullptr; + return UNKNOWN_ERROR; + } + this->sampleMmap->base = static_cast(currentMap); + this->sampleMmap->fd = fd; + return SUCCESS; +} + +int KUNPENG_PMU::PerfSampler::ReadInit() +{ + if (!this->sampleMmap->base) { + return UNKNOWN_ERROR; + } + return RingbufferReadInit(*this->sampleMmap.get()); +} + +void KUNPENG_PMU::PerfSampler::UpdatePidInfo(const pid_t &pid, const int &tid) +{ + auto findProc = procMap.find(tid); + if (findProc == procMap.end()) { + auto procTopo = GetProcTopology(tid); + if (procTopo != nullptr) { + DBG_PRINT("Add to proc map: %d\n", tid); + procMap[tid] = shared_ptr(procTopo, FreeProcTopo); + } + } +} + +void KUNPENG_PMU::PerfSampler::RawSampleProcess( + struct PmuData *current, PerfSampleIps *ips, union KUNPENG_PMU::PerfEvent *event) +{ + if (current == nullptr) { + return; + } + KUNPENG_PMU::PerfRawSample *sample = (KUNPENG_PMU::PerfRawSample *)event->sample.array; + // Copy ips from ring buffer and get stack info later. + for (__u64 i = 0; i < sample->nr; ++i) { + ips->ips.push_back(sample->ips[i]); + } + current->cpu = static_cast(sample->cpu); + current->pid = static_cast(sample->pid); + current->tid = static_cast(sample->tid); +} + +void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vector &sampleIps) +{ + union KUNPENG_PMU::PerfEvent *event; + while (true) { + event = this->SampleReadEvent(); + if (__glibc_unlikely(event == nullptr)) { + break; + } + __u32 sampleType = event->header.type; + switch (sampleType) { + case PERF_RECORD_SAMPLE: { + data.emplace_back(PmuData{0}); + auto& current = data.back(); + sampleIps.emplace_back(PerfSampleIps()); + auto& ips = sampleIps.back(); + this->RawSampleProcess(¤t, &ips, event); + break; + } + case PERF_RECORD_MMAP: { + SymResolverUpdateModule(event->mmap.tid, event->mmap.filename, event->mmap.addr); + break; + } + case PERF_RECORD_MMAP2: { + SymResolverUpdateModule(event->mmap2.tid, event->mmap2.filename, event->mmap2.addr); + break; + } + case PERF_RECORD_FORK: { + DBG_PRINT("Fork ptid: %d tid: %d\n", event->fork.pid, event->fork.tid); + UpdatePidInfo(event->fork.pid, event->fork.tid); + break; + } + default: + break; + } + PerfMmapConsume(*this->sampleMmap); + } + PerfMmapReadDone(*this->sampleMmap); +} + +void KUNPENG_PMU::PerfSampler::FillComm(const size_t &start, const size_t &end, vector &data) +{ + for (size_t i = start; i < end; ++i) { + auto& pmuData = data[i]; + auto findProc = procMap.find(pmuData.tid); + if (findProc == procMap.end()) { + continue; + } + pmuData.comm = findProc->second->comm; + } +} + +int KUNPENG_PMU::PerfSampler::Read(vector &data, std::vector &sampleIps) +{ + auto err = this->ReadInit(); + if (__glibc_unlikely(err != SUCCESS)) { + return err; + } + auto cnt = data.size(); + this->ReadRingBuffer(data, sampleIps); + if (this->pid == -1) { + FillComm(cnt, data.size(), data); + } + + return SUCCESS; +} + +int KUNPENG_PMU::PerfSampler::Init() +{ + auto err = this->MapPerfAttr(); + if (err != SUCCESS) { + return err; + } + err = this->Mmap(); + if (__glibc_unlikely(err != SUCCESS)) { + close(this->fd); + return LIBPERF_ERR_FAIL_MMAP; + } + return SUCCESS; +} diff --git a/pmu/sampler.h b/pmu/sampler.h new file mode 100644 index 0000000..a17ac02 --- /dev/null +++ b/pmu/sampler.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Gan + * Create: 2024-04-03 + * Description: definition of class PerfSampler for sampling and processing performance data in + * the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_SAMPLE_H +#define PMU_SAMPLE_H + +#include +#include +#include +#include +#include +#include +#include +#include "pmu_event.h" +#include "evt.h" +#include "symbol.h" + +namespace KUNPENG_PMU { + struct MmapParam { + int prot; + __u64 mask; + }; + + class PerfSampler : public PerfEvt { + public: + using PerfEvt::PerfEvt; + ~PerfSampler() + {} + + int Init() override; + int Read(std::vector &data, std::vector &sampleIps) override; + bool Close() override; + + int MapPerfAttr() override; + + private: + int ReadInit(); + int Mmap(); + union PerfEvent *SampleReadEvent(); + void RawSampleProcess(struct PmuData *sampleHead, PerfSampleIps *ips, union KUNPENG_PMU::PerfEvent *event); + void MmapSampleProcess(); + void Mmap2SampleProcess(); + void ForkSampleProcess(); + void ReadRingBuffer(std::vector &data, std::vector &sampleIps); + void FillComm(const size_t &start, const size_t &end, std::vector &data); + void UpdatePidInfo(const pid_t &pid, const int &tid); + + static int pages; + std::shared_ptr sampleMmap = std::make_shared(); + }; +} // namespace KUNPENG_PMU +#endif -- Gitee From 8604315cac19bb426cbbb36ff735d272165a0287 Mon Sep 17 00:00:00 2001 From: DCHii <13780064348@163.com> Date: Mon, 8 Apr 2024 15:54:07 +0800 Subject: [PATCH 03/14] Build scripts and related configurations, Provide methods. --- CMakeLists.txt | 53 +++++++++ Common.cmake | 75 ++++++++++++ build.sh | 145 +++++++++++++++++++++++ build/common.sh | 74 ++++++++++++ util/CMakeLists.txt | 22 ++++ util/common.cpp | 69 +++++++++++ util/common.h | 24 ++++ util/cpu_map.cpp | 105 +++++++++++++++++ util/cpu_map.h | 39 +++++++ util/linked_list.h | 76 ++++++++++++ util/log.h | 36 ++++++ util/pcerr.cpp | 86 ++++++++++++++ util/pcerr.h | 90 +++++++++++++++ util/process_map.cpp | 269 +++++++++++++++++++++++++++++++++++++++++++ util/process_map.h | 31 +++++ util/safe_handler.h | 72 ++++++++++++ util/util_time.cpp | 24 ++++ util/util_time.h | 26 +++++ 18 files changed, 1316 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 Common.cmake create mode 100644 build.sh create mode 100644 build/common.sh create mode 100644 util/CMakeLists.txt create mode 100644 util/common.cpp create mode 100644 util/common.h create mode 100644 util/cpu_map.cpp create mode 100644 util/cpu_map.h create mode 100644 util/linked_list.h create mode 100644 util/log.h create mode 100644 util/pcerr.cpp create mode 100644 util/pcerr.h create mode 100644 util/process_map.cpp create mode 100644 util/process_map.h create mode 100644 util/safe_handler.h create mode 100644 util/util_time.cpp create mode 100644 util/util_time.h diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4e98a59 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,53 @@ +# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. +# gala-gopher 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.Dai +# Create: 2024-04-03 +# Description: Define the overall structure and behavior of the project. + +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif (POLICY CMP0048) +set(PROJECT_TOP_DIR ${CMAKE_CURRENT_LIST_DIR}) +project(libkprof) + +cmake_minimum_required (VERSION 3.12.0) + +set(CMAKE_CXX_STANDARD 11) +if (INCLUDE_TEST) + set(CMAKE_CXX_STANDARD 14) +endif() + +set(CMAKE_C_STANDARD 11) +set(CMAKE_INSTALL_PREFIX "${PROJECT_TOP_DIR}/output/test" CACHE PATH "Installation directory" FORCE) + +if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.5) + message(FATAL_ERROR "GCC 4.8.5 or newer required") +endif() + +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +if (BUILD_TESTS) + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") +endif() + +set(TOP_DIR ${PROJECT_SOURCE_DIR}) + +message("TOP_DIR is ${TOP_DIR}") +include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) +# add_subdirectory(util) +add_subdirectory(symbol) +add_subdirectory(pmu) +if (INCLUDE_TEST) + add_subdirectory(test) +endif() + +set(CMAKE_EXPORT_COMPILE_COMMANDS True) \ No newline at end of file diff --git a/Common.cmake b/Common.cmake new file mode 100644 index 0000000..acf93e7 --- /dev/null +++ b/Common.cmake @@ -0,0 +1,75 @@ +# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. +# gala-gopher 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.Dai +# Create: 2024-04-03 +# Description: Set general configuration information. + + + +set(CMAKE_CXX_FLAGS_DEBUG " -O0 -g -Wall -pg -fno-gnu-unique -DDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE " -O3 -DNDEBUG -fstack-protector-all -Wl,-z,relro,-z,now -Wall -fPIE -s -fno-gnu-unique") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g") +set(CMAKE_CXX_FLAGS_MINSIZEREL "-Os -DNDEBUG") +set(CMAKE_C_FLAGS_DEBUG " -O0 -g -Wall -pg -fno-gnu-unique -DDEBUG") +set(CMAKE_C_FLAGS_RELEASE " -O3 -DNDEBUG -fstack-protector-all -Wl,-z,relro,-z,now -Wall -fPIE -s -fno-gnu-unique") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g") +set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") + +set(CMAKE_EXE_LINKER_FLAGS_RELEASE " -Wl,-z,relro,-z,now,-z,noexecstack -pie -s") +set(THIRD_PARTY ${PROJECT_TOP_DIR}/../opensource) +add_compile_options(-w) # 调试阶段先去除告警 + +if(NOT DEFINED OPEN_SOURCE_DIR) + set(OPEN_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../opensource") +endif() + +# 添加一个library +#GIT_REPOSITORY ssh://git@codehub-dg-y.huawei.com:2222/hwsecurec_group/huawei_secure_c.git +#GIT_TAG tag_Huawei_Secure_C_V100R001C01SPC012B002_00001 +add_library(securec STATIC IMPORTED) +set_property(TARGET securec PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/huawei_secure_c/lib/libsecurec.a) +include_directories(${THIRD_PARTY}/huawei_secure_c/include) + +# GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/www.sqlite.org/sqlite.git +# GIT_TAG 3.40.1 +add_library(sqlite3_share STATIC IMPORTED) +set_property(TARGET sqlite3_share PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/sqlite3/lib/libsqlite3.so) +include_directories(${OPEN_SOURCE_DIR}/local/sqlite3/include) + +add_library(elf_static STATIC IMPORTED) +set_property(TARGET elf_static PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/elfin-parser/libelf++.a) + +add_library(dwarf_static STATIC IMPORTED + include/pcerrc.h + util/pcerr.h + symbol/name_resolve.cpp + symbol/name_resolve.h + symbol/symbol.cpp + symbol/symbol.h + symbol/symbol_resolve.cpp) +set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/elfin-parser/libdwarf++.a) +include_directories(${THIRD_PARTY}/elfin-parser/dwarf) +include_directories(${THIRD_PARTY}/elfin-parser/elf) + +#GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/nlohmann/json.git +set(nlohmann_json_SOURCE_DIR ${OPEN_SOURCE_DIR}/json) +message("nlohmann_json_SOURCE_DIR:${nlohmann_json_SOURCE_DIR}") + +#GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/google/googletest.git +#GIT_TAG release-1.12.1 # +include_directories("${OPEN_SOURCE_DIR}/local/googletest/include") +add_library(gtest_main STATIC IMPORTED) +set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgtest_main.a) +add_library(gtest STATIC IMPORTED) +set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgtest.a) +add_library(gmock_main STATIC IMPORTED) +set_property(TARGET gmock_main PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock_main.a) +add_library(gmock STATIC IMPORTED) +set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock.a) \ No newline at end of file diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..3a462c5 --- /dev/null +++ b/build.sh @@ -0,0 +1,145 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. +# gala-gopher 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.Dai +# Create: 2024-04-03 +# Description: Building mainstream processes. + +set -e +set -x +if [ -d ${WORKSPACE}/code ]; then + PROJECT_DIR=${WORKSPACE}/code/libprof +else + CURRENT_DIR=$(cd $(dirname "$0"); pwd) + PROJECT_DIR=$(realpath "${CURRENT_DIR}") +fi +if [[ -d "${PROJECT_DIR}/../opensource" ]] ;then + OPENSOURCE_DIR=$(realpath "${PROJECT_DIR}/../opensource") +else + OPENSOURCE_DIR=${PROJECT_DIR}/opensource +fi +PACKAGE_NAME="DevKit-LibProf-24.0.RC1-Linux-Kunpeng" +PACKAGE_PATH="${PROJECT_DIR}/output/${PACKAGE_NAME}" +BUILD_DIR=${PROJECT_DIR}/_build +source ${PROJECT_DIR}/build/common.sh + +creat_dir "${BUILD_DIR}" +creat_dir "${PACKAGE_PATH}" +# 指定所有依赖使用的gcc +export CC=gcc +export CXX=g++ +if [ -d "${OPENSOURCE_DIR}/local" ];then + echo ${OPENSOURCE_DIR} "is exist" + else + echo ${OPENSOURCE_DIR} "is not exist" + mkdir ${OPENSOURCE_DIR}/local +fi + +# 默认情况下不包含测试目录 +INCLUDE_TEST=OFF + +# 如果第一个参数是 "test",则设置 INCLUDE_TEST 为 ON +if [[ "$1" == "test" ]]; then + build_googletest $OPENSOURCE_DIR + INCLUDE_TEST=ON +fi + +# build libprof.so libraries including libprocfs.so libprocfs.a libpmu.so libpmu.a libtrace.so libtrace.so +function build_elfin() { + local cmake_target_dir=$OPENSOURCE_DIR/local/elfin-parser + rm -rf ${cmake_target_dir} + if [ -d "${cmake_target_dir}" ];then + echo ${cmake_target_dir} "is exist" + return + else + echo ${cmake_target_dir} "is not exist" + mkdir ${cmake_target_dir} + fi + cd "$OPENSOURCE_DIR/elfin-parser" + rm -rf build + sed -i 's/-mcpu=tsv110//g' Common.cmake + sed -i 's/-mno-outline-atomics//g' Common.cmake + sed -i 's/-march=armv8.2-a//g' Common.cmake + if ! grep -q "^add_compile_options(-Wno-error=switch-enum)" CMakeLists.txt; then + sed -i '1i\add_compile_options(-Wno-error=switch-enum)' CMakeLists.txt + fi + mkdir build + cd build + cmake -DCMAKE_INSTALL_PREFIX=${cmake_target_dir} -DCMAKE_CXX_FLAGS="-fPIC" .. + make --silent -j 64 +# mkdir -p ${cmake_target_dir} + cp ./lib64/libdwarf++.a ./lib64/libelf++.a ${cmake_target_dir} +# popd + echo "install log path: $cmake_target_dir" + cd ../../../libprof +} + +function build_huawei_securec(){ + if [ -d "${OPENSOURCE_DIR}/local" ];then + echo ${OPENSOURCE_DIR} "is exist" + else + echo ${OPENSOURCE_DIR} "is not exist" + mkdir ${OPENSOURCE_DIR}/local + fi + local cmake_target_dir=$OPENSOURCE_DIR/local/huawei_secure_c + if [ -d "${cmake_target_dir}" ];then + echo ${cmake_target_dir} "is exist" + return + else + echo ${cmake_target_dir} "is not exist" + fi + pushd "$OPENSOURCE_DIR/huawei_secure_c/src" + sed -i 's/-Wdate-time//g' Makefile + sed -i 's/-Wduplicated-branches//g' Makefile + sed -i 's/-Wduplicated-cond//g' Makefile + sed -i 's/-Wimplicit-fallthrough=3//g' Makefile + sed -i 's/-Wshift-negative-value//g' Makefile + sed -i 's/-Wshift-overflow=2//g' Makefile + make lib + mkdir -p $cmake_target_dir + cp -rf "$OPENSOURCE_DIR/huawei_secure_c/lib" $cmake_target_dir + cp -rf "$OPENSOURCE_DIR/huawei_secure_c/include" $cmake_target_dir + popd + echo "install log path: $cmake_target_dir" +} + +build_libprof() +{ + cd $BUILD_DIR + cmake -DINCLUDE_TEST=$INCLUDE_TEST .. + make -j ${cpu_core_num} + echo "build_libprof success" +} + +build_package() +{ + cd $PROJECT_DIR/output + cp -rf ${PROJECT_DIR}/include ${PACKAGE_PATH} + cp -rf ${PROJECT_DIR}/symbol/symbol.h ${PACKAGE_PATH}/include/ + cp -rf ${BUILD_DIR}/pmu/libperf.so ${PACKAGE_PATH} + cp -rf ${BUILD_DIR}/symbol/libsym.so ${PACKAGE_PATH} + tar -czf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}" --owner=root --group=root + if [ "${IS_BUILDCHECK}" != "true" -a -d "${WORKSPACE}/output_${ENV_PIPELINE_JOB_ID}/libprof" ];then + cp -rf ${PACKAGE_NAME}.tar.gz ${WORKSPACE}/output_${ENV_PIPELINE_JOB_ID}/libprof + fi +} + +main() { + cd $PROJECT_DIR/ && python "build/generate_config.py" + build_elfin + build_huawei_securec + build_sqlite3 $OPENSOURCE_DIR + build_libprof + build_package +# bash ${CURRENT_DIR}/test/test.sh ${PROJECT_DIR}/output/test ${PACKAGE_PATH} +} + +# bash build.sh test来获取UT +main $@ \ No newline at end of file diff --git a/build/common.sh b/build/common.sh new file mode 100644 index 0000000..e19365e --- /dev/null +++ b/build/common.sh @@ -0,0 +1,74 @@ +#!/bin/bash +# Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. +# gala-gopher 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.Dai +# Create: 2024-04-03 +# Description: Partial methods for building scripts. +set -e + +cpu_core_num=$(($(nproc)-1)) + +creat_dir(){ + local target_dir="$1" + if [ -d "${target_dir}" ];then + rm -rf ${target_dir} + fi + mkdir -p ${target_dir} +} + +function build_googletest(){ + local open_source_dir=$1 + local cmake_target_dir=$1/local/googletest + if [ -d "${cmake_target_dir}" ];then + echo ${cmake_target_dir} "is exist" + return + else + echo ${cmake_target_dir} "is not exist" + fi + pushd "$open_source_dir/googletest" + mkdir -p build + pushd build + cmake -DCMAKE_INSTALL_PREFIX=$cmake_target_dir -DCMAKE_CXX_FLAGS="-fPIC" .. + make -j ${cpu_core_num} + make install + echo "install log path: $cmake_target_dir" +} + +function build_sqlite3(){ + local open_source_dir=$1 + local cmake_target_dir=$1/local/sqlite3 + if [ -d "${cmake_target_dir}" ];then + echo ${cmake_target_dir} "is exist" + return + else + echo ${cmake_target_dir} "is not exist" + fi + pushd "$open_source_dir/sqlite_src" + rm -rf sqlite-src-3370200 + unzip sqlite-src-3370200.zip + cd sqlite-src-3370200 + + patch -p1 < ../0001-sqlite-no-malloc-usable-size.patch + patch -p1 < ../0002-remove-fail-testcase-in-no-free-fd-situation.patch + patch -p1 < ../0003-CVE-2022-35737.patch + patch -p1 < ../0004-fix-memory-problem-in-the-rtree-test-suite.patch + patch -p1 < ../0005-fix-integer-overflow-on-gigabyte-string.patch + patch -p1 < ../0006-CVE-2022-46908.patch + patch -p1 < ../backport-CVE-2023-36191.patch + patch -p1 < ../backport-CVE-2023-7104.patch + + bash ./configure --prefix=${cmake_target_dir} --disable-threadsafe --disable-tcl --enable-fts5 --enable-json1 CFLAGS="-O2 -DSQLITE_ENABLE_FTS3=1 -DSQLITE_ENABLE_FTS4=1 -DSQLITE_ENABLE_RTREE=1 -fstack-protector-all -Wl,-z,relro,-z,now -s -fPIC -ftrapv" LDFLAGS="-pie -s -Wl,-z,relro,-z,now" + make -j ${cpu_core_num} + make install + + popd + rm -rf $open_source_dir/sqlite_src/sqlite-src-3370200 + echo "install sqlite3 path: $cmake_target_dir" +} \ No newline at end of file diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt new file mode 100644 index 0000000..9e57c45 --- /dev/null +++ b/util/CMakeLists.txt @@ -0,0 +1,22 @@ +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif (POLICY CMP0048) +project(libprofutil) +cmake_minimum_required (VERSION 3.12.0) + +set(UTIL_FILE_DIR ${PROJECT_TOP_DIR}/util) +set(SYMBOL_FILE_DIR ${PROJECT_TOP_DIR}/symbol) +set(INCLUDE_DIR ${PROJECT_TOP_DIR}/include) + +file(GLOB UTIL_SRC ${UTIL_FILE_DIR}/*cpp) + +include_directories(${SYMBOL_FILE_DIR}) +include_directories(${UTIL_FILE_DIR}) +include_directories(${INCLUDE_DIR}) + +include_directories(${THIRD_PARTY}/json/single_include/nlohmann) +include_directories(${THIRD_PARTY}/huawei_secure_c/include) + +add_library(profu STATIC ${UTIL_SRC}) +target_compile_options(profu PUBLIC -fPIC) +target_link_libraries(profu securec cap numa) \ No newline at end of file diff --git a/util/common.cpp b/util/common.cpp new file mode 100644 index 0000000..369ba94 --- /dev/null +++ b/util/common.cpp @@ -0,0 +1,69 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Provide common file operation functions and system resource management functions. + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include "pcerrc.h" +#include "common.h" + +std::string GetRealPath(const std::string filePath) +{ + char resolvedPath[PATH_MAX]; + if (realpath(filePath.data(), resolvedPath) == nullptr) { + return std::string{}; + } + if (access(resolvedPath, R_OK) != 0) { + return std::string{}; + } + return resolvedPath; +} + +bool IsValidPath(const std::string& filePath) +{ + if (filePath.empty()) { + return false; + } + return true; +} + +int RaiseNumFd(unsigned long numFd) +{ + unsigned long extra = 50; + unsigned long setNumFd = extra + numFd; + struct rlimit currentlim; + if (getrlimit(RLIMIT_NOFILE, ¤tlim) == -1) { + return LIBPERF_ERR_RAISE_FD; + } + if (currentlim.rlim_cur > setNumFd) { + return SUCCESS; + } + if (currentlim.rlim_max < numFd) { + return LIBPERF_ERR_TOO_MANY_FD; + } + struct rlimit rlim { + .rlim_cur = currentlim.rlim_max, .rlim_max = currentlim.rlim_max, + }; + if (setNumFd < currentlim.rlim_max) { + rlim.rlim_cur = setNumFd; + } + if (setrlimit(RLIMIT_NOFILE, &rlim) != 0) { + return LIBPERF_ERR_RAISE_FD; + } else { + return SUCCESS; + } +} \ No newline at end of file diff --git a/util/common.h b/util/common.h new file mode 100644 index 0000000..a5c629a --- /dev/null +++ b/util/common.h @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Provide common file operation functions and system resource management functions. + ******************************************************************************/ + +#ifndef LIBKPROF_COMMON_H +#define LIBKPROF_COMMON_H +#include + +std::string GetRealPath(const std::string filePath); +bool IsValidPath(const std::string& filePath); +int RaiseNumFd(uint64_t numFd); + +#endif // LIBKPROF_COMMON_H diff --git a/util/cpu_map.cpp b/util/cpu_map.cpp new file mode 100644 index 0000000..4f2f901 --- /dev/null +++ b/util/cpu_map.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Get CPU topology and chip type. + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "common.h" +#include "pcerr.h" +#include "cpu_map.h" + +using namespace std; + +static const std::string CPU_TOPOLOGY_PACKAGE_ID = "/sys/bus/cpu/devices/cpu%d/topology/physical_package_id"; +static const std::string MIDR_EL1 = "/sys/devices/system/cpu/cpu0/regs/identification/midr_el1"; +static const std::string KUNPENG920_ID = "0x00000000481fd010"; +static const std::string KUNPENG920B_ID = "0x00000000480fd020"; +static constexpr int PATH_LEN = 256; + +static CHIP_TYPE g_chipType = CHIP_TYPE::UNDEFINED_TYPE; + +static inline bool ReadCpuPackageId(int coreId, CpuTopology* cpuTopo) +{ + char filename[PATH_LEN]; + if (snprintf_s(filename, PATH_LEN + 1, PATH_LEN, CPU_TOPOLOGY_PACKAGE_ID.c_str(), coreId) < 0) { + return false; + } + std::string realPath = GetRealPath(filename); + if (!IsValidPath(realPath)) { + return false; + } + std::ifstream packageFile(realPath); + if (!packageFile.is_open()) { + return false; + } + std::string packageId; + packageFile >> packageId; + try { + cpuTopo->socketId = std::stoi(packageId); + } catch (...) { + return false; + } + return true; +} + +struct CpuTopology* GetCpuTopology(int coreId) +{ + auto cpuTopo = std::unique_ptr(new CpuTopology()); + memset_s(cpuTopo.get(), sizeof(CpuTopology), 0, sizeof(CpuTopology)); + if (coreId == -1) { + cpuTopo->coreId = coreId; + cpuTopo->numaId = -1; + cpuTopo->socketId = -1; + return cpuTopo.release(); + } + + if (!ReadCpuPackageId(coreId, cpuTopo.get())) { + return nullptr; + } + + cpuTopo->coreId = coreId; + cpuTopo->numaId = numa_node_of_cpu(coreId); + return cpuTopo.release(); +} + +bool InitCpuType() +{ + static std::mutex mtx; + std::lock_guard lock(mtx); + std::ifstream cpuFile(MIDR_EL1); + std::string cpuId; + cpuFile >> cpuId; + if (cpuId.compare(KUNPENG920_ID) == 0) { + g_chipType = CHIP_TYPE::KUNPENG_920; + } else if (cpuId.compare(KUNPENG920B_ID) == 0) { + g_chipType = CHIP_TYPE::KUNPENG_920B; + } else { + pcerr::New(LIBPERF_ERR_CHIP_TYPE_INVALID, "invalid chip type"); + return false; + } + return true; +} + +CHIP_TYPE GetCpuType() +{ + if (g_chipType == UNDEFINED_TYPE && !InitCpuType()) { + return UNDEFINED_TYPE; + } + return g_chipType; +} \ No newline at end of file diff --git a/util/cpu_map.h b/util/cpu_map.h new file mode 100644 index 0000000..b7f810c --- /dev/null +++ b/util/cpu_map.h @@ -0,0 +1,39 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Get CPU topology and chip type. + ******************************************************************************/ +#ifndef CPU_MAP_H +#define CPU_MAP_H +#include +#include "pmu.h" +#ifdef __cplusplus +extern "C" { +#endif + +enum CHIP_TYPE { + UNDEFINED_TYPE = 0, + KUNPENG_920 = 1, + KUNPENG_920B = 2, +}; + +struct CpuTopology { + int coreId; + int numaId; + int socketId; +}; +struct CpuTopology* GetCpuTopology(int coreId); +CHIP_TYPE GetCpuType(); +#ifdef __cplusplus +} +#endif +#endif diff --git a/util/linked_list.h b/util/linked_list.h new file mode 100644 index 0000000..3b3f034 --- /dev/null +++ b/util/linked_list.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: A series of universal single -connection list operation functions are implemented.. + ******************************************************************************/ +#ifndef LINKED_LIST +#define LINKED_LIST +#include +#include +#include +#include "securec.h" + +// Function to create a new node +template +ListNode* CreateNode() +{ + ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); + auto ret = memset_s(newNode, sizeof(ListNode), 0, sizeof(ListNode)); + if (ret != EOK) { + return nullptr; + } + if (newNode == nullptr) { + return nullptr; + } + + newNode->next = nullptr; + return newNode; +} + +// Function to add a node at the head of the linked list +template +void addHead(ListNode** head, ListNode* newNode) +{ + newNode->next = *head; + *head = newNode; +} + +// Function to add a node at the tail of the linked list +template +void AddTail(ListNode** head, ListNode** newNode) +{ + if (*head == nullptr) { + *head = *newNode; + } else { + ListNode* current = *head; + while (current->next != nullptr) { + current = current->next; + } + current->next = *newNode; + } +} + +// Function to free the linked list +template +void FreeList(ListNode** head) +{ + ListNode* current = *head; + ListNode* nextNode; + + while (current != nullptr) { + nextNode = current->next; + free(current); + current = nextNode; + } + *head = nullptr; // Ensure the head is set to nullptr after freeing all nodes +} +#endif \ No newline at end of file diff --git a/util/log.h b/util/log.h new file mode 100644 index 0000000..2a86061 --- /dev/null +++ b/util/log.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Used for code debugging. + ******************************************************************************/ +#ifndef PERF_LOG_H +#define PERF_LOG_H + +#ifdef NDEBUG +#define DBG_PRINT(...) +#define ERR_PRINT(...) +#else +#define DBG_PRINT(...) if (g_perfDebug) fprintf(stdout, __VA_ARGS__) +#define ERR_PRINT(...) if (g_perfDebug) fprintf(stderr, __VA_ARGS__) + +static int g_perfDebug = 0; + +__attribute__((constructor)) static void InitLog() +{ + auto debugEnv = getenv("PERF_DEBUG"); + if (debugEnv != nullptr && strcmp(debugEnv, "1") == 0) { + g_perfDebug = 1; + } +} +#endif + +#endif \ No newline at end of file diff --git a/util/pcerr.cpp b/util/pcerr.cpp new file mode 100644 index 0000000..ad4fcb7 --- /dev/null +++ b/util/pcerr.cpp @@ -0,0 +1,86 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Error code mechanism, used to return error codes and error messages. + ******************************************************************************/ +#include +#include "pcerrc.h" +#include "pcerr.h" + +namespace pcerr { + static std::unordered_map defaultMsg = { + {SUCCESS, "success"}, + {COMMON_ERR_NOMEM, "not enough memory"}, + {LIBPERF_ERR_NO_AVAIL_PD, "no available pd for libperf"}, + {LIBPERF_ERR_CHIP_TYPE_INVALID, "invalid cpu arch"}, + {LIBPERF_ERR_FAIL_LISTEN_PROC, "failed to listen process"}, + {LIBPERF_ERR_INVALID_CPULIST, "invalid cpu list"}, + {LIBPERF_ERR_INVALID_PIDLIST, "invalid pid list"}, + {LIBPERF_ERR_INVALID_EVTLIST, "invalid event list"}, + {LIBPERF_ERR_INVALID_PD, "invalid pd"}, + {LIBPERF_ERR_INVALID_EVENT, "invalid event"}, + {LIBPERF_ERR_SPE_UNAVAIL, "spe unavailable"}, + {LIBPERF_ERR_FAIL_GET_CPU, "failed to get cpu info"}, + {LIBPERF_ERR_FAIL_GET_PROC, "failed to get process info"}, + {LIBPERF_ERR_NO_PERMISSION, "no permission to open pmu device"}, + {LIBPERF_ERR_DEVICE_BUSY, "pmu device is busy"}, + {LIBPERF_ERR_DEVICE_INVAL, "invalid event for pmu device"}, + {LIBPERF_ERR_FAIL_MMAP, "failed to mmap"}, + {LIBPERF_ERR_FAIL_RESOLVE_MODULE, "failed to resolve symbols"}, + {LIBPERF_ERR_INVALID_PID, "failed to find process by pid"}, + {LIBPERF_ERR_INVALID_TASK_TYPE, "invalid task type"}, + {LIBPERF_ERR_INVALID_TIME, "invalid sampling time"}, + {LIBPERF_ERR_NO_PROC, "no such process"}, + {LIBPERF_ERR_KERNEL_NOT_SUPPORT, "current pmu task is not supported by kernel"}, + {LIBPERF_ERR_TOO_MANY_FD, "too many open files"}, + {LIBPERF_ERR_RAISE_FD, "failed to setrlimit or getrlimit"}, + }; + + ProfErrorObj ProfErrorObj::profErrorObj; + + ProfErrorObj& ProfErrorObj::GetInstance() + { + return profErrorObj; + } + + void ProfErrorObj::SetProfError(const ProfError& profError) + { + this->profError = profError; + } + + void New(int code) + { + auto findMsg = defaultMsg.find(code); + if (findMsg != defaultMsg.end()) { + New(code, findMsg->second); + } else { + New(code, ""); + } + } + + void New(int code, const std::string& msg) + { + ProfError profError = std::make_shared(code, msg); + ProfErrorObj::GetInstance().SetProfError(profError); + } + +} // namespace pcerr + +int Perrorno() +{ + return pcerr::ProfErrorObj::GetInstance().GetProfError()->Code(); +} + +const char* Perror() +{ + return pcerr::ProfErrorObj::GetInstance().GetProfError()->Msg(); +} \ No newline at end of file diff --git a/util/pcerr.h b/util/pcerr.h new file mode 100644 index 0000000..98f225c --- /dev/null +++ b/util/pcerr.h @@ -0,0 +1,90 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Error code mechanism, used to return error codes and error messages. + ******************************************************************************/ +#ifndef SYMBOL_PCERR_H +#define SYMBOL_PCERR_H +#include +#include +#include +#include +#include "pcerrc.h" + +namespace pcerr { + namespace details { + struct BaseError; + } + + using ProfError = std::shared_ptr; + + namespace details { + struct BaseError : std::enable_shared_from_this { + virtual ~BaseError() = 0; + virtual const char* Msg() const + { + return nullptr; + } + virtual int Code() const + { + return 0; + } + ProfError GetThisError() + { + return shared_from_this(); + } + }; + inline BaseError::~BaseError() + {} + } // namespace details + + namespace details { + class CodeStrMsgError : public BaseError { + public: + CodeStrMsgError(int code, const std::string& msg) : m_code{code}, m_msg{msg} + {} + int Code() const override + { + return this->m_code; + }; + const char* Msg() const override + { + return this->m_msg.c_str(); + }; + + private: + int m_code; + std::string m_msg; + }; + } // namespace details + class ProfErrorObj { + public: + static ProfErrorObj& GetInstance(); + void SetProfError(const ProfError& profError); + ProfError& GetProfError() + { + if (profError == nullptr) { + profError = std::make_shared(0, "success"); + } + return profError; + } + + private: + ProfError profError; + static ProfErrorObj profErrorObj; + }; + + void [[nodiscard]] New(int code); + void [[nodiscard]] New(int code, const std::string& msg); +} // namespace pcerr + +#endif \ No newline at end of file diff --git a/util/process_map.cpp b/util/process_map.cpp new file mode 100644 index 0000000..dcbaa91 --- /dev/null +++ b/util/process_map.cpp @@ -0,0 +1,269 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Get process and thread information. + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "common.h" +#include "process_map.h" + +using namespace std; +constexpr int COMM_SIZE = 128; +constexpr int PATH_LEN = 1024; +unsigned int GetNumPid() +{ + DIR *directory = opendir("/proc"); + struct dirent *entry; + unsigned int count = 0; + if (directory == nullptr) { + perror("Error opening /proc directory"); + return -1; + } + + // Count the number of process directories (pidList) + while ((entry = readdir(directory))) { + // Check if the entry is a directory and represents a process ID + if (entry->d_type == DT_DIR && atoi(entry->d_name) != 0) { + count++; + } + } + closedir(directory); + return count; +} + +int *GetAllPids(unsigned int *count) +{ + DIR *directory; + struct dirent *entry; + int *pidList = nullptr; + directory = opendir("/proc"); + + *count = GetNumPid(); + + // Allocate memory for storing pidList + if ((*count) < SIZE_MAX / sizeof(int)) { + pidList = static_cast(malloc((*count) * sizeof(int))); + } + if (pidList == nullptr) { + perror("Memory allocation error"); + closedir(directory); + return nullptr; + } + + int index = 0; + while ((entry = readdir(directory))) { + if (entry->d_type == DT_DIR && atoi(entry->d_name) != 0) { + pidList[index++] = atoi(entry->d_name); + } + } + + closedir(directory); + return pidList; +} + +void FreeProcTopo(struct ProcTopology *procTopo) +{ + if (procTopo == nullptr) { + return; + } + if (procTopo->childPid != nullptr) { + free(procTopo->childPid); + procTopo->childPid = nullptr; + } + if (procTopo->comm != nullptr) { + free(procTopo->comm); + procTopo->comm = nullptr; + } + if (procTopo->exe != nullptr) { + free(procTopo->exe); + procTopo->exe = nullptr; + } + delete procTopo; +} + +static int GetTgid(pid_t pid) +{ + if (pid == -1) { + // for system sampling. + return -1; + } + // Get tgid from /proc//status. + std::string filePath = "/proc/" + std::to_string(pid) + "/status"; + std::string realPath = GetRealPath(filePath); + if (!IsValidPath(realPath)) { + return -1; + } + std::ifstream statusFile(realPath); + if (!statusFile.is_open()) { + return -1; + } + string token; + bool foundTgid = false; + while (!statusFile.eof()) { + if (!statusFile.is_open()) { + return -1; + } + statusFile >> token; + if (token == "Tgid:") { + foundTgid = true; + continue; + } + if (foundTgid) { + return stoi(token); + } + } + return -1; +} + +static char *GetComm(pid_t pid) +{ + std::string commName; + if (pid == -1) { + commName = "system"; + char *comm = static_cast(malloc(commName.length() + 1)); + if (comm == nullptr) { + return nullptr; + } + if (strcpy_s(comm, COMM_SIZE, commName.c_str()) != EOK) { + return nullptr; + } + return comm; + } + std::string filePath = "/proc/" + std::to_string(pid) + "/comm"; + std::string realPath = GetRealPath(filePath); + if (!IsValidPath(realPath)) { + return nullptr; + } + std::ifstream commFile(realPath); + if (!commFile.is_open()) { + return nullptr; + } + commFile >> commName; + char *comm = static_cast(malloc(commName.length() + 1)); + if (comm == nullptr) { + return nullptr; + } + if (strcpy_s(comm, COMM_SIZE, commName.c_str()) != EOK) { + return nullptr; + } + return comm; +} + +struct ProcTopology *GetProcTopology(pid_t pid) +{ + unique_ptr procTopo(new ProcTopology{0}, FreeProcTopo); + procTopo->tid = pid; + try { + // Get tgid, i.e., process id. + procTopo->pid = GetTgid(pid); + if (pid != -1 && procTopo->pid == -1) { + return nullptr; + } + // Get command name. + procTopo->comm = GetComm(procTopo->pid); + if (procTopo->comm == nullptr) { + return nullptr; + } + } catch (exception&) { + return nullptr; + } + + return procTopo.release(); +} + +// Check if a string represents a valid integer +int IsValidInt(const char *str) +{ + while (*str) { + if (!isdigit(*str)) { + return 0; + } + str++; + } + return 1; +} + +void StoreThreadId(int** childTidList, int* count, const char* entryName) +{ + (*count)++; + int* newChildTidList = new int[(*count)]; + if (newChildTidList != nullptr) { + if (*childTidList != nullptr) { + std::copy(*childTidList, *childTidList + (*count) - 1, newChildTidList); + delete[] *childTidList; + } + newChildTidList[(*count) - 1] = atoi(entryName); + *childTidList = newChildTidList; + } +} + +bool GetChildTidRecursive(const char *dirPath, int **childTidList, int *count) +{ + DIR *dir = opendir(dirPath); + if (!dir) { + return false; + } + + struct dirent *entry; + while ((entry = readdir(dir)) != nullptr) { + if (entry->d_type == DT_DIR) { + // Skip "." and ".." directories + if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { + continue; + } + + // Check if the entry name is a valid thread ID + if (IsValidInt(entry->d_name)) { + // Store the thread ID in the array + StoreThreadId(childTidList, count, entry->d_name); + } + + char path[PATH_LEN]; + if (!snprintf_s(path, PATH_LEN + 1, sizeof(path), "%s/%s", dirPath, entry->d_name)) { + continue; + } + + // Continue recursively + GetChildTidRecursive(path, childTidList, count); + } + } + + closedir(dir); + return true; +} + +int *GetChildTid(int pid, int *numChild) +{ + int *childTidList = nullptr; + char dirPath[PATH_LEN]; + if (!snprintf_s(dirPath, PATH_LEN + 1, sizeof(dirPath), "/proc/%d/task", pid)) { + return nullptr; + } + *numChild = 0; + if (!GetChildTidRecursive(dirPath, &childTidList, numChild)) { + if (childTidList) { + delete[] childTidList; + childTidList = nullptr; + } + *numChild = 0; + } + return childTidList; +} + diff --git a/util/process_map.h b/util/process_map.h new file mode 100644 index 0000000..937d992 --- /dev/null +++ b/util/process_map.h @@ -0,0 +1,31 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Get process and thread information. + ******************************************************************************/ +#ifndef PROCESS_MAP_H +#define PROCESS_MAP_H +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct ProcTopology* GetProcTopology(pid_t pid); +void FreeProcTopo(struct ProcTopology *procTopo); +int* GetAllPids(int* count); +unsigned int GetNumPid(); +int* GetChildTid(int pid, int* numChild); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/util/safe_handler.h b/util/safe_handler.h new file mode 100644 index 0000000..302a5eb --- /dev/null +++ b/util/safe_handler.h @@ -0,0 +1,72 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Provide the key value processing function of thread security. + ******************************************************************************/ +#ifndef SAFE_HANDLER_H +#define SAFE_HANDLER_H +#include +#include +#include +#include + +template +class SafeHandler { +public: + SafeHandler() + {} + void tryLock(const Key& key) + { + while (true) { + if (find(key)) { + continue; + } + { + std::unique_lock lock(_mutex); + if (find(key)) { + continue; + } + insert(key); + } + break; + } + } + + void releaseLock(const Key& key) + { + std::unique_lock lock(_smutex); + _set.erase(key); + } + +private: + std::mutex _mutex; + std::mutex _smutex; + std::unordered_set _set; + + void insert(const Key& key) + { + std::unique_lock lock(_smutex); + _set.insert(key); + } + + bool find(const Key& key) + { + std::unique_lock lock(_smutex); + if (_set.find(key) != _set.end()) { + return true; + } + return false; + } +}; + +#endif + diff --git a/util/util_time.cpp b/util/util_time.cpp new file mode 100644 index 0000000..8f63e83 --- /dev/null +++ b/util/util_time.cpp @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Get current time. + ******************************************************************************/ +#include +#include "util_time.h" + +int64_t GetCurrentTime() +{ + return std::chrono::duration_cast( + std::chrono::high_resolution_clock::now().time_since_epoch()) + .count(); +} + diff --git a/util/util_time.h b/util/util_time.h new file mode 100644 index 0000000..42a3d28 --- /dev/null +++ b/util/util_time.h @@ -0,0 +1,26 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Wang + * Create: 2024-04-03 + * Description: Get current time. + ******************************************************************************/ +#ifndef UTIL_TIME_H +#define UTIL_TIME_H +#ifdef __cplusplus +#include +#include +extern "C" { +#endif +int64_t GetCurrentTime(); +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file -- Gitee From fd3da01321afd6713ebf516cf72d2307bbd2895d Mon Sep 17 00:00:00 2001 From: Jin Yao <2589309608@qq.com> Date: Mon, 8 Apr 2024 16:28:50 +0800 Subject: [PATCH 04/14] spe: Add initial ARM SPE support Arm SPE is a hardware-assisted CPU profiling mechanism that provides detailed profiling capabilities. It records key execution data, including program counters, data addresses, and PMU events1. SPE enhances performance analysis for branches, memory access, and more, making it useful for software optimization. This patch adds the basic support of ARM packet decoding, the spe ring buffer and aux buffer processing. Signed-off-by: Jin Yao <2589309608@qq.com> --- pmu/spe.cpp | 609 ++++++++++++++++++++++++++++++++++++++++++++ pmu/spe.h | 226 ++++++++++++++++ pmu/spe_sampler.cpp | 188 ++++++++++++++ pmu/spe_sampler.h | 57 +++++ 4 files changed, 1080 insertions(+) create mode 100644 pmu/spe.cpp create mode 100644 pmu/spe.h create mode 100644 pmu/spe_sampler.cpp create mode 100644 pmu/spe_sampler.h diff --git a/pmu/spe.cpp b/pmu/spe.cpp new file mode 100644 index 0000000..44bdd34 --- /dev/null +++ b/pmu/spe.cpp @@ -0,0 +1,609 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Jin + * Create: 2024-04-03 + * Description: implements functionalities for reading and processing System Performance Events (SPE) + * data in the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "pmu_event.h" +#include "arm_spe_decoder.h" +#include "profile.h" +#include "process_map.h" +#include "log.h" +#include "pcerr.h" +#include "evt.h" +#include "spe.h" + +using namespace std; +using namespace KUNPENG_PMU; + +constexpr unsigned SPE_RECORD_MAX = 100000; +constexpr unsigned BUFF_SIZE = 64; +/* Should align to 2^n size in pages */ +constexpr unsigned RING_BUF_SIZE = 64 * 1024; +constexpr unsigned AUX_BUF_SIZE = 256 * 1024; + + +struct AuxContext { + struct ContextSwitchData *dummyData; + int *dummyIdx; + int cpu; + size_t auxOffset; + size_t auxSize; +}; + +static int OpenSpeEvent(PmuEvt *pmuAttr, int cpu) +{ + struct perf_event_attr attr = {0}; + + attr.size = sizeof(attr); + attr.type = pmuAttr->type; + attr.config = pmuAttr->config; /* pa_enable | load_filter | store_filter | ts_enable */ + attr.config1 = pmuAttr->config1; /* event_filter */ + attr.config2 = pmuAttr->config2; /* min_latency */ + attr.exclude_guest = 1; + attr.disabled = 1; + attr.freq = pmuAttr->useFreq; + attr.sample_period = pmuAttr->period; + attr.sample_type = PERF_SAMPLE_TID; + attr.sample_id_all = 1; + attr.read_format = PERF_FORMAT_ID; + + return PerfEventOpen(&attr, -1, cpu, -1, 0); +} + +static int OpenDummyEvent(int cpu) +{ + struct perf_event_attr attr = {0}; + + attr.size = sizeof(attr); + attr.type = PERF_TYPE_SOFTWARE; + attr.config = PERF_COUNT_SW_DUMMY; + attr.exclude_kernel = 1; + attr.disabled = 1; + attr.sample_period = 1; + attr.sample_type = PERF_SAMPLE_TIME; + attr.sample_id_all = 1; + attr.read_format = PERF_FORMAT_ID; + attr.context_switch = 1; + attr.mmap = 1; + attr.task = 1; + attr.inherit = 1; + attr.exclude_guest = 1; + + return PerfEventOpen(&attr, -1, cpu, -1, 0); +} + +static int PerfReadTscConversion(const struct perf_event_mmap_page *pc, struct PerfTscConversion *tc) +{ + uint32_t seq; + int i = 0; + + while (1) { + seq = pc->lock; + RMB(); + tc->timeMult = pc->time_mult; + tc->timeShift = pc->time_shift; + tc->timeZero = pc->time_zero; + tc->capUserTimeZero = pc->cap_user_time_zero; + RMB(); + + if (pc->lock == seq && !(seq & 1)) { + break; + } + if (++i > 10000) { + return LIBPERF_ERR_KERNEL_NOT_SUPPORT; + } + } + + if (!tc->capUserTimeZero) { + return LIBPERF_ERR_KERNEL_NOT_SUPPORT; + } + + return SUCCESS; +} + +static uint64_t TscToPerfTime(uint64_t cyc, struct PerfTscConversion *tc) +{ + uint64_t quot = cyc >> tc->timeShift; + uint64_t rem = cyc & ((static_cast(1) << tc->timeShift) - 1); + + return tc->timeZero + quot * tc->timeMult + ((rem * tc->timeMult) >> tc->timeShift); +} + +static void CoreSpeClose(struct SpeCoreContext *ctx, struct SpeContext *speCtx) +{ + if (ctx->speFd > 0) { + close(ctx->speFd); + } + + if (ctx->dummyFd > 0) { + close(ctx->dummyFd); + } + + if (ctx->speMpage && ctx->speMpage != MAP_FAILED) { + munmap(ctx->speMpage, speCtx->speMmapSize); + } + + if (ctx->auxMpage && ctx->auxMpage != MAP_FAILED) { + munmap(ctx->auxMpage, speCtx->auxMmapSize); + } + + if (ctx->dummyMpage && ctx->dummyMpage != MAP_FAILED) { + munmap(ctx->dummyMpage, speCtx->dummyMmapSize); + } + + memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx)); +} + +static int CoreSpeOpenFailed(struct SpeCoreContext **ctx, struct SpeContext *speCtx) +{ + CoreSpeClose(*ctx, speCtx); + return LIBPERF_ERR_SPE_UNAVAIL; +} + +static int CoreSpeOpen(struct SpeCoreContext **ctx, struct SpeContext *speCtx, PmuEvt *attr, int cpu) +{ + int ret = -1; + struct perf_event_mmap_page *mp = nullptr; + + (*ctx)->cpu = cpu; + (*ctx)->speFd = OpenSpeEvent(attr, cpu); + if ((*ctx)->speFd < 0) { + auto err = MapErrno(errno); + ERR_PRINT("failed to open spe\n"); + CoreSpeClose(*ctx, speCtx); + return err; + } + DBG_PRINT("perf_event_open, cpu: %d fd: %d\n", cpu, (*ctx)->speFd); + + (*ctx)->speMpage = mmap(nullptr, speCtx->speMmapSize, PROT_READ | PROT_WRITE, MAP_SHARED, (*ctx)->speFd, 0); + if ((*ctx)->speMpage == MAP_FAILED) { + ERR_PRINT("failed to mmap for spe event\n"); + return CoreSpeOpenFailed(ctx, speCtx); + } + + mp = static_cast((*ctx)->speMpage); + mp->aux_offset = speCtx->speMmapSize; + mp->aux_size = speCtx->auxMmapSize; + + (*ctx)->auxMpage = mmap(nullptr, mp->aux_size, PROT_READ | PROT_WRITE, MAP_SHARED, (*ctx)->speFd, mp->aux_offset); + if ((*ctx)->auxMpage == MAP_FAILED) { + ERR_PRINT("failed to mmap for aux event\n"); + return CoreSpeOpenFailed(ctx, speCtx); + } + + (*ctx)->dummyFd = OpenDummyEvent(cpu); + if ((*ctx)->dummyFd < 0) { + ERR_PRINT("failed to open dummy event fd\n"); + return CoreSpeOpenFailed(ctx, speCtx); + } + + (*ctx)->dummyMpage = mmap(nullptr, speCtx->dummyMmapSize, PROT_READ | PROT_WRITE, MAP_SHARED, (*ctx)->dummyFd, 0); + if ((*ctx)->dummyMpage == MAP_FAILED) { + ERR_PRINT("failed to mmap for dummy event\n"); + return CoreSpeOpenFailed(ctx, speCtx); + } + + return SUCCESS; +} + +int SpeOpen(PmuEvt *attr, int cpu, SpeContext *ctx) +{ + int pageSize = sysconf(_SC_PAGESIZE); + + if (attr->type == -1) { + free(ctx); + return LIBPERF_ERR_SPE_UNAVAIL; + } + + ctx->cpuNum = 1; + ctx->pageSize = pageSize; + + /* Should align to 2^n size in pages */ + ctx->speMmapSize = RING_BUF_SIZE + static_cast(pageSize); + ctx->auxMmapSize = AUX_BUF_SIZE; + ctx->dummyMmapSize = RING_BUF_SIZE + pageSize; + + ctx->coreCtxes = (struct SpeCoreContext *)malloc(sizeof(struct SpeCoreContext)); + ctx->coreCtxes->mask = ctx->auxMmapSize - 1; + ctx->coreCtxes->prev = 0; + if (!ctx->coreCtxes) { + free(ctx); + return COMMON_ERR_NOMEM; + } + + auto err = CoreSpeOpen(&ctx->coreCtxes, ctx, attr, cpu); + if (err != 0) { + free(ctx->coreCtxes); + free(ctx); + return err; + } + return SUCCESS; +} + +static int CoreSpeEnable(struct SpeCoreContext *ctx) +{ + if (ctx->dummyFd <= 0 || ctx->speFd <= 0) { + return -1; + } + + ioctl(ctx->dummyFd, PERF_EVENT_IOC_ENABLE, 0); + ioctl(ctx->speFd, PERF_EVENT_IOC_ENABLE, 0); + return 0; +} + +int SpeEnable(struct SpeContext *ctx) +{ + for (int i = 0; i < ctx->cpuNum; i++) { + CoreSpeEnable(&ctx->coreCtxes[i]); + } + + return 0; +} + +static int CoreSpeDisable(struct SpeCoreContext *ctx) +{ + if (ctx->dummyFd <= 0 || ctx->speFd <= 0) { + return -1; + } + + ioctl(ctx->speFd, PERF_EVENT_IOC_DISABLE, 0); + ioctl(ctx->dummyFd, PERF_EVENT_IOC_DISABLE, 0); + return 0; +} + +void SpeDisable(struct SpeContext *ctx) +{ + for (int i = 0; i < ctx->cpuNum; i++) { + CoreSpeDisable(&ctx->coreCtxes[i]); + } + + return; +} + +void SpeClose(struct SpeContext *ctx) +{ + for (int i = 0; i < ctx->cpuNum; i++) { + CoreSpeClose(&ctx->coreCtxes[i], ctx); + } + + free(ctx->coreCtxes); + free(ctx); + return; +} + +void Spe::UpdateProcMap(__u32 ppid, __u32 pid) +{ + auto findParent = procMap.find(ppid); + if (findParent != procMap.end()) { + auto procTopo = GetProcTopology(pid); + if (procTopo != nullptr) { + if (procMap.find(pid) == procMap.end()) { + DBG_PRINT("Add to proc map: %d\n", pid); + procMap[pid] = shared_ptr(procTopo, FreeProcTopo); + } else { + FreeProcTopo(procTopo); + } + } + } +} + +static void ParseContextSwitch(PerfEventSampleContextSwitch *contextSample, ContextSwitchData *data, uint64_t *num, + ContextSwitchData *lastSwitchOut) +{ + if (contextSample->header.misc == 0 && contextSample->time < 1e18) { + // Context switch for Switch-In, nextPrevPid> is active pid before time>. + // Use switch-in data for spe timestamp analysis, because switch-out data miss some context switch info. + data[*num].nextPrevPid = contextSample->nextPrevPid; + data[*num].nextPrevTid = contextSample->nextPrevTid; + data[*num].time = contextSample->time; + (*num)++; + } + if (contextSample->header.misc == PERF_RECORD_MISC_SWITCH_OUT && contextSample->time < 1e18) { + // keep track of the last switch-out data, which will be used for last time slice. + lastSwitchOut->nextPrevPid = contextSample->nextPrevPid; + lastSwitchOut->nextPrevTid = contextSample->nextPrevTid; + lastSwitchOut->time = contextSample->time; + (*num)++; + } +} + +void Spe::CoreDummyData(struct SpeCoreContext *context, struct ContextSwitchData *data, int size, int pageSize) +{ + uint64_t maxNum = size / sizeof(struct ContextSwitchData); + uint64_t num = 1; + struct perf_event_mmap_page *mpage = (struct perf_event_mmap_page *)context->dummyMpage; + uint8_t *ringBuf = (uint8_t *)(mpage) + pageSize; + uint64_t dataHead = mpage->data_head; + uint64_t dataTail = mpage->data_tail; + + RMB(); + ContextSwitchData lastSwitchOut; + while ((dataTail < dataHead) && (num < maxNum)) { + uint64_t off = dataTail % mpage->data_size; + struct perf_event_header *header = (struct perf_event_header *)(ringBuf + off); + + if (header->type == PERF_RECORD_MMAP) { + struct PerfRecordMmap *sample = (struct PerfRecordMmap *)header; + SymResolverUpdateModule(sample->tid, sample->filename, sample->addr); + dataTail += header->size; + continue; + } + if (header->type == PERF_RECORD_FORK) { + struct PerfRecordFork *sample = (struct PerfRecordFork *)header; + DBG_PRINT("Fork pid: %d tid: %d\n", sample->pid, sample->tid); + UpdateProcMap(sample->pid, sample->tid); + dataTail += header->size; + continue; + } + + if ((off + header->size) > mpage->data_size || header->type != PERF_RECORD_SWITCH_CPU_WIDE) { + /* skip the wrap record or invalid record */ + dataTail += header->size; + continue; + } + + struct PerfEventSampleContextSwitch *contextSample = (struct PerfEventSampleContextSwitch *)header; + ParseContextSwitch(contextSample, data, &num, &lastSwitchOut); + dataTail += header->size; + } + + // Put last switch out to the end of dummy data. + data[num].nextPrevPid = lastSwitchOut.nextPrevPid; + data[num].nextPrevTid = lastSwitchOut.nextPrevTid; + data[num].time = lastSwitchOut.time; + ++num; + data[0].num = num; + data[0].nextPrevPid = -1; + data[0].nextPrevTid = -1; + + mpage->data_tail = mpage->data_head; + MB(); +} + +static void SetTidByTimestamp(struct ContextSwitchData *dummyData, int *dummyIdx, struct SpeRecord *buf, + struct SpeRecord *bufEnd, int cpu, struct PerfTscConversion *tc) +{ + for (struct SpeRecord *start = buf; start < bufEnd; start++) { + uint64_t recordTime = TscToPerfTime(start->timestamp, tc); + + start->cpu = cpu; + start->timestamp = recordTime; + + if (*dummyIdx >= dummyData[0].num - 1) { + // Now, all spe records locate after the last switch-in data. + // We have to use switch-out data to get pid of the last time slice. + start->pid = dummyData[dummyData[0].num - 1].nextPrevPid; + start->tid = dummyData[dummyData[0].num - 1].nextPrevTid; + continue; + } + + for (; *dummyIdx < dummyData[0].num - 1; (*dummyIdx)++) { + if (dummyData[*dummyIdx].time > recordTime) { + // is located between dummyData[*dummyIdx-1].time and dummyData[*dummyIdx].time. + // Then pid is the prev pid of dummyData[*dummyIdx]. + start->pid = dummyData[*dummyIdx].nextPrevPid; + start->tid = dummyData[*dummyIdx].nextPrevTid; + break; + } + } + } + + return; +} + +static struct SpeRecord *CoreAuxData(struct SpeCoreContext *ctx, AuxContext *auxCtx, + struct SpeRecord *buf, int *remainSize) +{ + struct perf_event_mmap_page *mpage = (struct perf_event_mmap_page *)ctx->speMpage; + uint8_t *auxBuf = static_cast(ctx->auxMpage); + uint8_t *auxStart = auxBuf + auxCtx->auxOffset % mpage->aux_size; + uint8_t *auxEnd = auxStart + auxCtx->auxSize; + SpeRecord *bufEnd = SpeGetRecord(auxStart, auxEnd, buf, remainSize); + + struct PerfTscConversion tc; + auto err = PerfReadTscConversion(mpage, &tc); + if (err != SUCCESS) { + pcerr::New(err); + return nullptr; + } + SetTidByTimestamp(auxCtx->dummyData, auxCtx->dummyIdx, buf, bufEnd, auxCtx->cpu, &tc); + + return bufEnd; +} + +static size_t ComputeAuxSize(size_t auxMapLen, size_t headOff, size_t oldOff, int pageSize) +{ + // Compute current aux buffer size by current offset and previous offset. + size_t size = 0; + if (headOff > oldOff) { + // Normal case, just diff of two offsets. + size = headOff - oldOff; + } else { + // Wraparound, size equals sum of two segment. + size = auxMapLen - (oldOff - headOff); + } + return size; +} + +static struct SpeRecord *CoreSpeData(struct SpeCoreContext *ctx, struct ContextSwitchData *dummyData, + struct SpeRecord *buf, int *remainSize, int pageSize, int cpu) +{ + int dummyIdx = 1; + struct perf_event_mmap_page *mpage = (struct perf_event_mmap_page *)ctx->speMpage; + RMB(); + __u64 old = ctx->prev; + __u64 head = ReadOnce(&mpage->aux_head); + if (old == head) { + return buf; + } + size_t headOff = head & ctx->mask; + size_t oldOff = old & ctx->mask; + size_t size = ComputeAuxSize(mpage->aux_size, headOff, oldOff, pageSize); + + size_t auxOffset = 0; + struct SpeRecord *bufEnd = nullptr; + AuxContext auxCtx = {.dummyData = dummyData, + .dummyIdx = &dummyIdx, + .cpu = cpu}; + if (size > headOff) { + // Wraparound, read two data segments. + // Read the tail segment. + auxCtx.auxSize = size - headOff; + auxCtx.auxOffset = mpage->aux_size - auxCtx.auxSize; + buf = CoreAuxData(ctx, &auxCtx, buf, remainSize); + // Read the head segment. + auxCtx.auxOffset = 0; + auxCtx.auxSize = headOff; + buf = CoreAuxData(ctx, &auxCtx, buf, remainSize); + } else { + auxCtx.auxOffset = oldOff; + auxCtx.auxSize = size; + buf = CoreAuxData(ctx, &auxCtx, buf, remainSize); + } + ctx->prev = head; + + mpage->data_tail = mpage->data_head; + mpage->aux_tail = mpage->aux_head; + MB(); + + return buf; +} + +/* + * For the initial implementation, caller should allocate a big enough buffer to + * contain all of spe records. It's not pretty frankly, will be improved later. + */ +int Spe::SpeReadData(struct SpeContext *context, struct SpeRecord *buf, int size) +{ + int remainSize = size; + int dummySize = context->dummyMmapSize; + CoreDummyData(context->coreCtxes, dummyData, dummySize, context->pageSize); + buf = CoreSpeData(context->coreCtxes, dummyData, buf, &remainSize, context->pageSize, cpu); + return size - remainSize; +} + +int Spe::Open(PmuEvt *attr) +{ + if (status == NONE) { + ctx = (struct SpeContext *)malloc(sizeof(struct SpeContext)); + if (!ctx) { + return COMMON_ERR_NOMEM; + } + auto err = SpeOpen(attr, cpu, ctx); + if (err != SUCCESS) { + return err; + } + status |= OPENED; + this->dummyFd = this->ctx->coreCtxes->dummyFd; + this->fd = this->ctx->coreCtxes->speFd; + + if (records == nullptr) { + records = new SpeRecord[SPE_RECORD_MAX]; + } + if (dummyData == nullptr) { + dummyData = new ContextSwitchData[ctx->dummyMmapSize]; + } + } + + return SUCCESS; +} +bool Spe::Enable(bool clearPrevRecords) +{ + if (clearPrevRecords) { + pidRecords.clear(); + } + + if (!(status & OPENED)) { + return false; + } + if (status & ENABLED) { + return true; + } + SpeEnable(ctx); + status &= ~DISABLED; + status &= ~READ; + status |= ENABLED; + return true; +} +bool Spe::Disable() +{ + if (!(status & OPENED)) { + return false; + } + if (status & DISABLED) { + return true; + } + SpeDisable(ctx); + status &= ~ENABLED; + status |= DISABLED; + return true; +} +bool Spe::Close() +{ + if (status == NONE) { + return true; + } + SpeClose(ctx); + if (records != nullptr) { + delete[] records; + records = nullptr; + } + if (dummyData != nullptr) { + delete[] dummyData; + dummyData = nullptr; + } + status = NONE; + return true; +} + +int Spe::Read() +{ + if (!(status & OPENED)) { + return UNKNOWN_ERROR; + } + if (status & READ) { + return SUCCESS; + } + int numRecord = SpeReadData(this->ctx, records, SPE_RECORD_MAX); + for (int i = 0; i < numRecord; i++) { + SpeRecord *rec = &records[i]; + pidRecords[rec->tid].push_back(rec); + } + status |= READ; + if (Perrorno() == LIBPERF_ERR_KERNEL_NOT_SUPPORT) { + return Perrorno(); + } + return SUCCESS; +} + +bool Spe::HaveRead() +{ + return status & READ; +} + +const std::vector Spe::GetPidRecords(const pid_t &pid) const +{ + auto findRecords = pidRecords.find(pid); + if (findRecords == pidRecords.end()) { + return {}; + } + return findRecords->second; +} diff --git a/pmu/spe.h b/pmu/spe.h new file mode 100644 index 0000000..23f39a0 --- /dev/null +++ b/pmu/spe.h @@ -0,0 +1,226 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Jin + * Create: 2024-04-03 + * Description: definition of class Spe for handling System Performance Events (SPE) data collection + * and processing for each CPU, storing the collected data for further analysis + ******************************************************************************/ +#ifndef __SPE__HH__ +#define __SPE__HH__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pmu_event.h" +#include "symbol.h" + +#define MB() asm volatile("dsb sy") +#define RMB() asm volatile("dsb ld") +#define WMB() asm volatile("dsb st") + +#define EVENT_EXCEPTION_GEN 0x1 +#define EVENT_RETIRED 0x2 +#define EVENT_L1D_ACCESS 0x4 +#define EVENT_L1D_REFILL 0x8 +#define EVENT_TLB_ACCESS 0x10 +#define EVENT_TLB_REFILL 0x20 +#define EVENT_NOT_TAKEN 0x40 +#define EVENT_MISPRED 0x80 +#define EVENT_LLC_ACCESS 0x100 +#define EVENT_LLC_REFILL 0x200 +#define EVENT_REMOTE_ACCESS 0x400 + +#define SPE_SAMPLE_MAX 200000 + +struct SpeCoreContext { + int cpu; + int speFd; + int dummyFd; + void *speMpage; + void *auxMpage; + void *dummyMpage; + __u64 prev; + size_t mask; +}; + +struct SpeContext { + int cpuNum; + __u64 speMmapSize; /* size of spe event ring buffer + first page */ + __u64 auxMmapSize; /* size of aux buffer */ + int dummyMmapSize; /* size of dummy event ring buffer + first page */ + int pageSize; + struct SpeCoreContext *coreCtxes; +}; + +struct SpeRecord { + uint64_t event; + uint64_t pid; + uint64_t tid; + int cpu; + int vaNid; + uint64_t va; + uint64_t pa; + uint64_t timestamp; + uint64_t pc; +}; + +struct PerfEventSample { + struct perf_event_header header; // only keep the header +}; + +struct PerfEventSampleAux { + struct perf_event_header header; // aux buffer event header + uint64_t auxOffset; // current offset in aux buffer + uint64_t auxSize; // current aux data size + uint64_t flags; // data flag + uint32_t pid, tid; // process and thread id of spe record +}; + +struct PerfEventSampleContextSwitch { + struct perf_event_header header; // context switch record header + uint32_t nextPrevPid; // The process ID of the previous (if switching in) or + // next (if switching out) process on the CPU. + uint32_t nextPrevTid; // The thread ID of the previous (if switching in) or + // next (if switching out) thread on the CPU. + uint64_t time; // timestamp +}; + +struct PerfTscConversion { + uint16_t timeShift; + uint32_t timeMult; + uint64_t timeZero; + int capUserTimeZero; +}; + +struct ContextSwitchData { + uint32_t nextPrevPid = 0; + uint32_t nextPrevTid = 0; + union { + uint64_t time = 0; + uint64_t num; + }; +}; + +struct SampleId { + __u32 pid, tid; /* if PERF_SAMPLE_TID set */ + __u64 time; /* if PERF_SAMPLE_TIME set */ + __u64 id; /* if PERF_SAMPLE_ID set */ + __u32 cpu, res; /* if PERF_SAMPLE_CPU set */ + __u64 identifier; /* if PERF_SAMPLE_IDENTIFIER set */ +}; + +/** + * @brief SPE collector for each cpu. + */ +class Spe { +public: + explicit Spe(int cpu, std::unordered_map> &procMap) + : cpu(cpu), procMap(procMap) + {} + + ~Spe() + { + if (records != nullptr) { + delete records; + records = nullptr; + } + } + + /** + * @brief Open SPE ring buffer. + * @param attr sampling attribute. + * @return true + * @return false + */ + int Open(PmuEvt *attr); + + /** + * @brief Start collect. + * @param clearPrevRecords whether clear all records from previos collection. + * @return true + * @return false + */ + bool Enable(bool clearPrevRecords = true); + + /** + * @brief Stop collect. + * @return true + * @return false + */ + bool Disable(); + + /** + * @brief Free ring buffer. + * @return true + * @return false + */ + bool Close(); + + /** + * @brief Read data in ring buffer in last collection, and store data in this object. Use GetPidRecords to get data. + * @return true + * @return false + */ + int Read(); + + /** + * @brief The last collceted data have been read. + * @return true + * @return false + */ + bool HaveRead(); + + /** + * @brief Get SPE data of process with . + * @param pid + * @return const std::vector + */ + const std::vector GetPidRecords(const pid_t &pid) const; + + int GetSpeFd() const + { + return fd; + } + + const std::map>& GetAllRecords() const + { + return pidRecords; + } + +private: + int SpeReadData(struct SpeContext *context, struct SpeRecord *buf, int size); + void CoreDummyData(struct SpeCoreContext *context, struct ContextSwitchData *data, int size, int pageSize); + void UpdateProcMap(__u32 ppid, __u32 pid); + + const unsigned short NONE = 0; + const unsigned short OPENED = 1 << 0; + const unsigned short ENABLED = 1 << 1; + const unsigned short DISABLED = 1 << 2; + const unsigned short READ = 1 << 3; + + int cpu = 0; + SpeContext *ctx = nullptr; + unsigned short status = NONE; + int dummyFd = 0; + int fd = 0; + SpeRecord *records = nullptr; + ContextSwitchData *dummyData = nullptr; + std::map> pidRecords; + std::unordered_map> &procMap; +}; + +#endif \ No newline at end of file diff --git a/pmu/spe_sampler.cpp b/pmu/spe_sampler.cpp new file mode 100644 index 0000000..7b6a39d --- /dev/null +++ b/pmu/spe_sampler.cpp @@ -0,0 +1,188 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Jin + * Create: 2024-04-03 + * Description: implements functions for handling System Performance Events (SPE) data collection + * and processing for each CPU in the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include +#include +#include +#include "linked_list.h" +#include "spe.h" +#include "pcerrc.h" +#include "spe_sampler.h" +#include "pcerr.h" + +#define MB() asm volatile("dsb sy"); +#define rmb() asm volatile("dsb ld"); +#define wmb() asm volatile("dsb st"); + +static constexpr int CYCLES_FREQ = 100; +static constexpr int SPE_PERIOD = 100; + +using namespace std; + +namespace KUNPENG_PMU { + + static map speSet; + + bool PerfSpe::Mmap() + { + return true; + } + + int PerfSpe::MapPerfAttr() + { + return SUCCESS; + } + + int PerfSpe::Init() + { + auto findSpe = speSet.find(this->cpu); + if (findSpe != speSet.end()) { + this->fd = findSpe->second.GetSpeFd(); + return SUCCESS; + } + + findSpe = speSet.emplace(this->cpu, Spe(this->cpu, procMap)).first; + auto err = findSpe->second.Open(evt); + if (err != SUCCESS) { + speSet.erase(this->cpu); + return err; + } + this->fd = findSpe->second.GetSpeFd(); + return SUCCESS; + } + + int PerfSpe::Read(vector &data, std::vector &sampleIps) + { + auto findSpe = speSet.find(this->cpu); + if (findSpe == speSet.end()) { + return LIBPERF_ERR_SPE_UNAVAIL; + } + + if (findSpe->second.HaveRead()) { + // Do not repeat reading data from the same core. + return SUCCESS; + } + if (findSpe->second.Read() != SUCCESS) { + return Perrorno(); + } + + // Fill pmu data from SPE collector. + auto cnt = data.size(); + if (pid == -1) { + // Loop over all tids in records and resolve module symbol for all pids. + UpdatePidList(findSpe->second); + for (auto records : findSpe->second.GetAllRecords()) { + if (records.first == -1 || records.first == 0) { + continue; + } + // Insert each spe record for each tid. + InsertSpeRecords(records.first, records.second, data, sampleIps); + } + } else { + // Loop over all tids. + for (auto &proc : procMap) { + // Get all spe records for tid. + const auto &records = findSpe->second.GetPidRecords(proc.first); + InsertSpeRecords(proc.second->tid, records, data, sampleIps); + } + } + + return SUCCESS; + } + + void PerfSpe::InsertSpeRecords( + const int &tid, const std::vector &speRecords, vector &data, vector &sampleIps) + { + ProcTopology *procTopo = nullptr; + auto findProc = procMap.find(tid); + if (findProc == procMap.end()) { + return; + } + procTopo = findProc->second.get(); + // Use large memory malloc instead of small mallocs to improve performance. + PmuDataExt *extPtrs = new PmuDataExt[speRecords.size()]; + extPool.push_back(extPtrs); + for (size_t i = 0; i < speRecords.size(); ++i) { + auto rec = speRecords[i]; + data.emplace_back(PmuData{0}); + auto ¤t = data.back(); + current.pid = static_cast(procTopo->pid); + current.tid = static_cast(rec->tid); + current.cpu = static_cast(this->cpu); + current.ext = &extPtrs[i]; + current.ext->event = rec->event; + current.ext->va = rec->va; + current.ext->pa = rec->pa; + current.comm = procTopo ? procTopo->comm : nullptr; + // Assign pc, which will be parsed to Symbol in PmuRead. + sampleIps.emplace_back(PerfSampleIps()); + auto &ips = sampleIps.back(); + ips.ips.push_back(rec->pc); + } + } + + void PerfSpe::UpdatePidList(const Spe &spe) + { + for (auto records : spe.GetAllRecords()) { + auto tid = records.first; + if (procMap.find(tid) == procMap.end()) { + auto procTopo = GetProcTopology(tid); + if (procTopo != nullptr) { + procMap[tid] = shared_ptr(procTopo, FreeProcTopo); + } + } + } + } + + bool PerfSpe::Enable() + { + auto findSpe = speSet.find(this->cpu); + if (findSpe == speSet.end()) { + return false; + } + + return findSpe->second.Enable(); + } + + bool PerfSpe::Disable() + { + auto findSpe = speSet.find(this->cpu); + if (findSpe == speSet.end()) { + return false; + } + + return findSpe->second.Disable(); + } + + bool PerfSpe::Close() + { + auto findSpe = speSet.find(this->cpu); + if (findSpe == speSet.end()) { + return true; + } + + findSpe->second.Close(); + speSet.erase(this->cpu); + for (auto extPtr : extPool) { + delete[] extPtr; + } + extPool.clear(); + return true; + } + + +} // namespace KUNPENG_PMU \ No newline at end of file diff --git a/pmu/spe_sampler.h b/pmu/spe_sampler.h new file mode 100644 index 0000000..2a45463 --- /dev/null +++ b/pmu/spe_sampler.h @@ -0,0 +1,57 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Jin + * Create: 2024-04-03 + * Description: defines a class PerfSpe for handling System Performance Events (SPE) data collection + * and processing in the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_SPE_H +#define PMU_SPE_H + +#include +#include +#include +#include +#include +#include +#include "process_map.h" +#include "pmu_event.h" +#include "evt.h" +#include "symbol.h" +#include "spe.h" +#include "arm_spe_decoder.h" + +namespace KUNPENG_PMU { + class PerfSpe : public PerfEvt { + public: + using PerfEvt::PerfEvt; + ~PerfSpe() + {} + + int Init() override; + int Read(std::vector &data, std::vector &sampleIps) override; + int MapPerfAttr() override; + bool Mmap(); + + bool Disable() override; + bool Enable() override; + bool Close() override; + + private: + bool SpeExist(int cpu) const; + void InsertSpeRecords(const int &tid, const std::vector &speRecords, std::vector &data, + std::vector &sampleIps); + void UpdatePidList(const Spe &spe); + + std::vector extPool; + }; +} // namespace KUNPENG_PMU +#endif -- Gitee From e70c2d40e1f86628623edb0479f8aa8a2727b226 Mon Sep 17 00:00:00 2001 From: echo <2220386943@qq.com> Date: Mon, 8 Apr 2024 19:11:08 +0800 Subject: [PATCH 05/14] fix symbol --- symbol/CMakeLists.txt | 22 + symbol/name_resolve.cpp | 28 + symbol/name_resolve.h | 28 + symbol/symbol.cpp | 155 ++++++ symbol/symbol.h | 134 +++++ symbol/symbol_resolve.cpp | 1106 +++++++++++++++++++++++++++++++++++++ symbol/symbol_resolve.h | 167 ++++++ 7 files changed, 1640 insertions(+) create mode 100644 symbol/CMakeLists.txt create mode 100644 symbol/name_resolve.cpp create mode 100644 symbol/name_resolve.h create mode 100644 symbol/symbol.cpp create mode 100644 symbol/symbol.h create mode 100644 symbol/symbol_resolve.cpp create mode 100644 symbol/symbol_resolve.h diff --git a/symbol/CMakeLists.txt b/symbol/CMakeLists.txt new file mode 100644 index 0000000..0338a95 --- /dev/null +++ b/symbol/CMakeLists.txt @@ -0,0 +1,22 @@ +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif (POLICY CMP0048) +project(libkprof) +cmake_minimum_required (VERSION 3.12.0) + +set(SYMBOL_FILE_DIR ${PROJECT_TOP_DIR}/symbol) +set(INCLUDE_DIR ${PROJECT_TOP_DIR}/include) +set(UTIL_FILE_DIR ${PROJECT_TOP_DIR}/util) + +file(GLOB SYMBOL_SRC ${SYMBOL_FILE_DIR}/*c ${SYMBOL_FILE_DIR}/*cpp ${UTIL_FILE_DIR}/pcerr.cpp) + +include_directories(${UTIL_FILE_DIR}) +include_directories(${SYMBOL_FILE_DIR}) +include_directories(${INCLUDE_DIR}) + +message(${THIRD_PARTY}/elfin-parser/elf) +include_directories(${THIRD_PARTY}/json/single_include/nlohmann) +include_directories(${THIRD_PARTY}/huawei_secure_c/include) + +ADD_LIBRARY(sym SHARED ${SYMBOL_SRC}) +target_link_libraries(sym elf_static dwarf_static securec pthread) diff --git a/symbol/name_resolve.cpp b/symbol/name_resolve.cpp new file mode 100644 index 0000000..204b392 --- /dev/null +++ b/symbol/name_resolve.cpp @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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-04-03 + * Description: Reverse the symbols in the C ++ ABI specification and convert it into a readable function name. + ******************************************************************************/ +#include +#include +#include +#include + +char* CppNameDemangle(const char* abiName) +{ + int status; + char* name = abi::__cxa_demangle(abiName, nullptr, nullptr, &status); + if (status != 0) { + return nullptr; + } + return name; +} diff --git a/symbol/name_resolve.h b/symbol/name_resolve.h new file mode 100644 index 0000000..70169ba --- /dev/null +++ b/symbol/name_resolve.h @@ -0,0 +1,28 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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-04-03 + * Description: Reverse the symbols in the C ++ ABI specification and convert it into a readable function name. + ******************************************************************************/ +#ifndef CPP_NAME_RESOLVE_H +#define CPP_NAME_RESOLVE_H + +#ifdef __cpusplus +extern "C" { +#endif +/** For further implementation such as support for python, rust or java name + * demangel, APIs should be implemented here */ +char* CppNamedDemangel(const char* abiName); +#ifdef __cpusplus +} +#endif + +#endif \ No newline at end of file diff --git a/symbol/symbol.cpp b/symbol/symbol.cpp new file mode 100644 index 0000000..29ba621 --- /dev/null +++ b/symbol/symbol.cpp @@ -0,0 +1,155 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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-04-03 + * Description: Provide the tool function and data structure of the complete symbol analysis and stack analysis. + ******************************************************************************/ +#include +#include +#include "symbol_resolve.h" +#include "pcerr.h" +#include "symbol.h" + +using namespace KUNPENG_SYM; +void SymResolverInit() +{ + SymbolResolve::GetInstance(); +} + +int SymResolverRecordKernel() +{ + try { + return SymbolResolve::GetInstance()->RecordKernel(); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +int SymResolverRecordModule(int pid) +{ + try { + return SymbolResolve::GetInstance()->RecordModule(pid, RecordModuleType::RECORD_ALL); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +int SymResolverRecordModuleNoDwarf(int pid) +{ + try { + return SymbolResolve::GetInstance()->RecordModule(pid, RecordModuleType::RECORD_NO_DWARF); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +int SymResolverRecordElf(const char* fileName) +{ + try { + return SymbolResolve::GetInstance()->RecordElf(fileName); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +int SymResolverRecordDwarf(const char* fileName) +{ + try { + return SymbolResolve::GetInstance()->RecordDwarf(fileName); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +void SymResolverDestroy() +{ + SymbolResolve::GetInstance()->Clear(); +} + +struct Stack* StackToHash(int pid, unsigned long* stack, int nr) +{ + try { + return SymbolResolve::GetInstance()->StackToHash(pid, stack, nr); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return nullptr; + } +} + +struct Symbol* SymResolverMapAddr(int pid, unsigned long addr) +{ + try { + return SymbolResolve::GetInstance()->MapAddr(pid, addr); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return nullptr; + } +} + +int SymResolverIncrUpdateModule(int pid) +{ + try { + return SymbolResolve::GetInstance()->UpdateModule(pid); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +int SymResolverUpdateModule(int pid, const char* moduleName, unsigned long startAddr) +{ + try { + return SymbolResolve::GetInstance()->UpdateModule(pid, moduleName, startAddr); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return COMMON_ERR_NOMEM; + } +} + +struct StackAsm* SymResolverAsmCode(const char* moduleName, unsigned long startAddr, unsigned long endAddr) +{ + try { + return SymbolResolve::GetInstance()->MapAsmCode(moduleName, startAddr, endAddr); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return nullptr; + } +} + +struct Symbol* SymResolverMapCodeAddr(const char* moduleName, unsigned long startAddr) +{ + try { + return SymbolResolve::GetInstance()->MapCodeAddr(moduleName, startAddr); + } catch (std::bad_alloc& err) { + pcerr::New(COMMON_ERR_NOMEM); + return nullptr; + } +} + +void FreeModuleData(int pid) +{ + return SymbolResolve::GetInstance()->FreeModule(pid); +} + +void FreeSymbolPtr(struct Symbol* symbol) +{ + SymbolUtils::FreeSymbol(symbol); +} + +void FreeAsmStack(struct StackAsm* stackAsm) +{ + SymbolUtils::FreeStackAsm(&stackAsm); +} diff --git a/symbol/symbol.h b/symbol/symbol.h new file mode 100644 index 0000000..173310f --- /dev/null +++ b/symbol/symbol.h @@ -0,0 +1,134 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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-04-03 + * Description: Provide the tool function and data structure of the complete symbol analysis and stack analysis. + ******************************************************************************/ +#ifndef SYMBOL_H +#define SYMBOL_H +#include +#include +#ifdef __cplusplus +extern "C" { +#endif + +struct Symbol { + unsigned long addr; // address (dynamic allocated) of this symbol + char* module; // binary name of which the symbol belongs to + char* symbolName; // name of the symbol + char* fileName; // corresponding file of current symbol + unsigned int lineNum; // line number of a symbol in the file + unsigned long offset; + unsigned long codeMapEndAddr; // function end address + unsigned long codeMapAddr; // real srcAddr of Asm Code or + __u64 count; +}; + +struct Stack { + struct Symbol* symbol; // symbol info for current stack + struct Stack* next; // points to next position in stack + __u64 count; +} __attribute__((aligned(64))); + +struct StackAsm { + char* funcName; // function name of void + unsigned long funcStartAddr; // start address of function + unsigned long functFileOffset; // offset of function in this file + struct StackAsm* next; // points to next position in stack + struct AsmCode* asmCode; // asm code +}; + +struct AsmCode { + unsigned long addr; // address of asm file + char* code; // code of asm + char* fileName; // this source file name of this asm code + unsigned int lineNum; // the real line of this addr +}; + +void SymResolverInit(); + +int SymResolverRecordKernel(); + +int SymResolverRecordModule(int pid); + +int SymResolverRecordModuleNoDwarf(int pid); +/** + * Incremental update modules of pid, i.e. record newly loaded dynamic libraries by pid. + */ +int SymResolverIncrUpdateModule(int pid); + +int SymResolverUpdateModule(int pid, const char* moduleName, unsigned long startAddr); + +/** + * Record ELF data for a binary + */ +int SymResolverRecordElf(const char* fileName); + +/** + * Record DWARF data for a binary + */ +int SymResolverRecordDwarf(const char* fileName); + +/** + * Clean up resolver in the end after usage + */ +void SymResolverDestroy(); + +/** + * Convert a callstack to a unsigned long long hashid + */ +struct Stack* StackToHash(int pid, unsigned long* stack, int nr); + +/** + * Map a specific address to a symbol + */ +struct Symbol* SymResolverMapAddr(int pid, unsigned long addr); + +/** + * Obtain assembly code from file and start address and end address + */ +struct StackAsm* SymResolverAsmCode(const char* moduleName, unsigned long startAddr, unsigned long endAddr); + +/** + * Obtain the source code from the file and real start address. + */ +struct Symbol* SymResolverMapCodeAddr(const char* moduleName, unsigned long startAddr); + +/** + * free Symbol pointer + */ +void FreeSymbolPtr(struct Symbol* symbol); + +/** + * free pid module data + * @param pid + */ +void FreeModuleData(int pid); + +/** + * free asm stack code + */ +void FreeAsmStack(struct StackAsm* stackAsm); + +struct ProcTopology { + int pid; + int tid; + int ppid; + int numChild; + int* childPid; + char* comm; + char* exe; + bool kernel; +}; +#ifdef __cplusplus +} +#endif +#endif diff --git a/symbol/symbol_resolve.cpp b/symbol/symbol_resolve.cpp new file mode 100644 index 0000000..8b8ba2c --- /dev/null +++ b/symbol/symbol_resolve.cpp @@ -0,0 +1,1106 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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-04-03 + * Description: Provide a complete set of symbolic analysis tools, perform operations such as + * module records, address analysis and stack conversion. + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "securec.h" +#include "name_resolve.h" +#include "pcerr.h" +#include "symbol_resolve.h" + +using namespace KUNPENG_SYM; +constexpr __u64 MAX_LINE_LENGTH = 1024; +constexpr __u64 MODULE_NAME_LEN = 1024; +constexpr __u64 MAX_LINUX_FILE_NAME = 1024; +constexpr __u64 MAX_LINUX_SYMBOL_LEN = 8192; +constexpr __u64 MAX_LINUX_MODULE_LEN = 1024; +constexpr int ADDR_LEN = 16; +constexpr int MODE_LEN = 5; +constexpr int MAP_LEN = 128; +constexpr int OFFSET_LEN = 16; +constexpr int DEV_LEN = 16; +constexpr int INODE_LEN = 16; +constexpr __u64 KERNEL_START_ADDR = 0xffff000000000000; +constexpr int BINARY_HALF = 2; +constexpr int KERNEL_NAME_LEN = 8; +constexpr int SHA_BIT_SHIFT_LEN = 8; +constexpr int KERNEL_MODULE_LNE = 128; +constexpr int CODE_LINE_RANGE_LEN = 10; +constexpr int HEX_LEN = 16; +constexpr int TO_TAIL_LEN = 2; + +const std::string HUGEPAGE = "/anon_hugepage"; +const std::string DEV_ZERO = "/dev/zero"; +const std::string ANON = "//anon"; +const std::string STACK = "[stack"; +const std::string SOCKET = "socket:"; +const std::string VSYSCALL = "[vsyscall]"; +const std::string HEAP = "[heap]"; +const std::string VDSO = "[vdso]"; +const std::string SYSV = "/sysv"; +const std::string VVAR = "[vvar]"; +const std::string R_XP = "r-xp"; +const std::string SLASH = "/"; +const char DASH = '-'; +const char EXE_TYPE = 'x'; + +namespace { + static inline void SetFalse(bool& flag) + { + flag = false; + } + + static inline bool CheckIfFile(std::string mapline) + { + return (!((mapline.find(HUGEPAGE) != std::string::npos) || (mapline.find(DEV_ZERO) != std::string::npos) || + (mapline.find(ANON) != std::string::npos) || (mapline.find(STACK) != std::string::npos) || + (mapline.find(SOCKET) != std::string::npos) || (mapline.find(VSYSCALL) != std::string::npos) || + (mapline.find(HEAP) != std::string::npos) || (mapline.find(VDSO) != std::string::npos) || + (mapline.find(SYSV) != std::string::npos) || (mapline.find(VVAR) != std::string::npos)) && + (mapline.find(R_XP) != std::string::npos)) + ? true + : false; + } + + static inline char* InitChar(int len) + { + char* str = new char[len + 1]; + auto ret = memset_s(str, len + 1, 0, len + 1); + if (ret != EOK) { + return nullptr; + } + if (str == nullptr) { + return nullptr; + } + return str; + } + + static void ReadProcPidMap(std::ifstream& file, std::vector>& modVec) + { + char line[MAX_LINUX_SYMBOL_LEN]; + char mode[MODE_LEN]; + char offset[OFFSET_LEN]; + char dev[DEV_LEN]; + char inode[INODE_LEN]; + while (file.getline(line, MAX_LINUX_SYMBOL_LEN)) { + if (!CheckIfFile(line)) { + continue; + } + std::shared_ptr data = std::make_shared(); + char modNameChar[MAX_LINUX_MODULE_LEN]; + if (!sscanf_s(line, "%lx-%lx %s %s %s %s %s", + &data->start, &data->end, mode, MODE_LEN, offset, OFFSET_LEN, dev, + DEV_LEN, inode, INODE_LEN, modNameChar, MODULE_NAME_LEN)) { + continue; + } + data->moduleName = modNameChar; + modVec.emplace_back(data); + } + } + + static inline void DwarfInfoRecord(DWARF_DATA_MAP& dwarfTable, const dwarf::line_table& lt, + SymbolVet& dwarfFileArray) + { + for (const dwarf::line_table::entry& line : lt) { + if (line.end_sequence) { + continue; + } + std::string fileName = line.file->path(); + if (fileName.empty()) { + continue; + } + DwarfMap data = {0}; + data.lineNum = line.line; + data.fileIndex = dwarfFileArray.InsertKeyForIndex(fileName); + dwarfTable.insert({line.address, data}); + } + } + + static inline void ElfInfoRecord(std::vector& elfVector, const elf::section& sec) + { + for (const auto& sym : sec.as_symtab()) { + auto& data = sym.get_data(); + if (data.type() != elf::stt::func) + continue; + ElfMap elfData; + elfData.start = data.value; + elfData.end = data.value + data.size; + elfData.symbolName = sym.get_name(); + elfVector.emplace_back(elfData); + } + } + + static StackAsm* FirstLineMatch(const std::string& line) + { + if (line[line.size() - TO_TAIL_LEN] == ':') { + struct StackAsm* stackAsm = CreateNode(); + stackAsm->funcName = InitChar(MAX_LINE_LENGTH); + stackAsm->asmCode = nullptr; + stackAsm->next = nullptr; + int ret = sscanf_s(line.c_str(), "%llx %s %*s %*s %llx", &stackAsm->funcStartAddr, stackAsm->funcName, + MAX_LINE_LENGTH, &stackAsm->functFileOffset); + if (ret <= 0) { + free(stackAsm->funcName); + stackAsm->funcName = nullptr; + free(stackAsm); + stackAsm = nullptr; + return nullptr; + } + return stackAsm; + } + return nullptr; + } + + static bool MatchFileName(const std::string& line, std::string& fileName, unsigned int& lineNum) + { + char startStr[MAX_LINE_LENGTH + 1]; + int ret = sscanf_s(line.c_str(), "%s", startStr, MAX_LINE_LENGTH); + if (ret < 0) { + return false; + } + std::string preLine = startStr; + if (preLine.find(":") + 1 >= preLine.size()) { + return false; + } + std::string lineStr = preLine.substr(preLine.find(":") + 1, preLine.size()); + if (lineStr.empty()) { + return false; + } + if (!SymbolUtils::IsNumber(lineStr)) { + return false; + } + try { + lineNum = std::atoi(lineStr.c_str()); + } catch (std::exception& err) { + return false; + } + fileName = preLine.substr(0, preLine.find(":")); + return true; + } + + static AsmCode* MatchAsmCode(const std::string& line, const std::string& srcFileName, unsigned int lineNum) + { + std::string addrStr = line.substr(0, line.find(":")); + struct AsmCode* asmCode = new AsmCode(); + asmCode->addr = SymbolUtils::SymStoul(addrStr); + if (asmCode->addr == 0) { + delete asmCode; + return nullptr; + } + size_t tailLen = line.find("\n") != std::string::npos ? line.find("\n") : strlen(line.c_str()); + char startStr[MAX_LINE_LENGTH + 1]; + int ret = sscanf_s(line.c_str(), "%*s %s", startStr, MAX_LINE_LENGTH); + if (ret < 0) { + delete asmCode; + return nullptr; + } + std::string codeStr = line.substr(line.find(startStr) + strlen(startStr), tailLen); + if (!codeStr.empty()) { + codeStr.erase(0, codeStr.find_first_not_of(" ")); + codeStr.erase(0, codeStr.find_first_not_of("\t")); + } + asmCode->code = InitChar(MAX_LINE_LENGTH); + if (strcpy_s(asmCode->code, MAX_LINE_LENGTH, codeStr.c_str()) != EOK) { + delete[] asmCode->code; + delete[] asmCode; + return nullptr; + } + asmCode->fileName = InitChar(MAX_LINUX_FILE_NAME); + if (strcpy_s(asmCode->fileName, MAX_LINUX_FILE_NAME, srcFileName.c_str()) != EOK) { + delete[] asmCode->fileName; + delete[] asmCode; + return nullptr; + } + asmCode->lineNum = lineNum; + return asmCode; + } + + static inline void FreeSymMap(SYMBOL_MAP& symMap) + { + for (auto& item : symMap) { + for (auto& data : item.second) { + SymbolUtils::FreeSymbol(data.second); + } + } + } + + static inline void FreeStackMap(STACK_MAP& stackMap) + { + for (auto& item : stackMap) { + for (auto& data : item.second) { + struct Stack* head = data.second; + FreeList(&head); + } + } + } + + static inline void FreeKernelVet(std::vector>& kernel) + { + for (auto symbol : kernel) { + if (symbol->module) { + delete[] symbol->module; + symbol->module = nullptr; + } + if (symbol->fileName) { + delete[] symbol->fileName; + symbol->fileName = nullptr; + } + if (symbol->symbolName) { + delete[] symbol->symbolName; + symbol->symbolName = nullptr; + } + } + } + + static inline size_t HashStr(unsigned long* stack, int nr) + { + std::string str = {}; + for (int k = 0; k < nr; k++) { + str += stack[k]; + } + std::hash h; + size_t n = h(str); + return n; + } + + static inline bool CheckColonSuffix(const std::string& str) + { + if (!strstr(str.c_str(), ":")) { + return false; + } + size_t colonIndex = str.find(":"); + if (colonIndex + 1 >= str.size()) { + return false; + } + std::string suffix = str.substr(colonIndex + 1, str.size()); + if (suffix.empty() || suffix.size() == 1) { + return false; + } + return true; + } + +} // namespace + +void SymbolUtils::FreeSymbol(struct Symbol* symbol) +{ + if (symbol->symbolName) { + delete[] symbol->symbolName; + symbol->symbolName = nullptr; + } + if (symbol->fileName) { + delete[] symbol->fileName; + symbol->fileName = nullptr; + } + if (symbol->module) { + delete[] symbol->module; + symbol->module = nullptr; + } + if (symbol) { + delete symbol; + symbol = nullptr; + } +} + +void SymbolUtils::FreeStackAsm(struct StackAsm** stackAsm) +{ + struct StackAsm* current = *stackAsm; + struct StackAsm* next; + while (current != nullptr) { + next = current->next; + if (current->asmCode) { + delete[] current->asmCode->code; + delete[] current->asmCode->fileName; + free(current->asmCode); + } + if (current->funcName) { + delete[] current->funcName; + } + free(current); + current = next; + } + *stackAsm = nullptr; +} + +bool SymbolUtils::IsFile(const char* fileName) +{ + struct stat st {}; + // 获取文件属性 + if (stat(fileName, &st) != 0) { + return false; + } + return (S_ISREG(st.st_mode)) ? true : false; +} + +unsigned long SymbolUtils::SymStoul(const std::string& addrStr) +{ + try { + return std::stoul(addrStr, nullptr, HEX_LEN); + } catch (std::invalid_argument& invalidErr) { + return 0; + } +} + +std::string SymbolUtils::RealPath(const std::string& filePath) +{ + char buff[PATH_MAX] = {0}; + if (realpath(filePath.c_str(), buff)) { + return std::string{buff}; + } else { + return std::string{}; + } +} + +bool SymbolUtils::IsValidPath(const std::string& filePath) +{ + if (filePath.empty()) { + return false; + } + return true; +} + +bool SymbolUtils::IsNumber(const std::string& str) +{ + for (int i = 0; i < str.length(); i++) { + if (!isdigit(str[i])) { + return false; + } + } + return true; +} + +int SymbolResolve::RecordModule(int pid, RecordModuleType recordModuleType) +{ + if (pid < 0) { + pcerr::New(LIBSYM_ERR_PARAM_PID_INVALID, "libsym param process ID must be greater than 0"); + return LIBSYM_ERR_PARAM_PID_INVALID; + } + SetFalse(this->isCleared); + moduleSafeHandler.tryLock(pid); + if (this->moduleMap.find(pid) != this->moduleMap.end()) { + pcerr::New(0, "success"); + moduleSafeHandler.releaseLock(pid); + return 0; + } + char mapFile[MAP_LEN]; + if (!snprintf_s(mapFile, MAP_LEN + 1, MAP_LEN, "/proc/%d/maps", pid)) { + moduleSafeHandler.releaseLock(pid); + return LIBSYM_ERR_SNPRINF_OPERATE_FAILED; + } + std::ifstream file(mapFile); + if (!file.is_open()) { + pcerr::New(LIBSYM_ERR_OPEN_FILE_FAILED, + "libsym can't open file named " + std::string{mapFile} + " because of " + std::string{strerror(errno)}); + moduleSafeHandler.releaseLock(pid); + return LIBSYM_ERR_OPEN_FILE_FAILED; + } + std::vector> modVec; + ReadProcPidMap(file, modVec); + for (auto& item : modVec) { + this->RecordElf(item->moduleName.c_str()); + if (recordModuleType != RecordModuleType::RECORD_NO_DWARF) { + this->RecordDwarf(item->moduleName.c_str()); + } + } + this->moduleMap.insert({pid, modVec}); + pcerr::New(0, "success"); + moduleSafeHandler.releaseLock(pid); + return 0; +} + +int SymbolResolve::UpdateModule(int pid) +{ + if (pid < 0) { + pcerr::New(LIBSYM_ERR_PARAM_PID_INVALID, "libsym param process ID must be greater than 0"); + return LIBSYM_ERR_PARAM_PID_INVALID; + } + moduleSafeHandler.tryLock(pid); + if (this->moduleMap.find(pid) == this->moduleMap.end()) { + // need to use RecordModule first! + pcerr::New(SUCCESS); + moduleSafeHandler.releaseLock(pid); + return SUCCESS; + } + // Get memory maps of pid. + char mapFile[MAP_LEN]; + if (!snprintf_s(mapFile, MAP_LEN + 1, MAP_LEN, "/proc/%d/maps", pid)) { + moduleSafeHandler.releaseLock(pid); + return LIBSYM_ERR_SNPRINF_OPERATE_FAILED; + } + std::ifstream file(mapFile); + if (!file.is_open()) { + pcerr::New(LIBSYM_ERR_OPEN_FILE_FAILED, + "libsym can't open file named " + std::string{mapFile} + " because of " + std::string{strerror(errno)}); + moduleSafeHandler.releaseLock(pid); + return LIBSYM_ERR_OPEN_FILE_FAILED; + } + std::vector> newModVec; + ReadProcPidMap(file, newModVec); + + // Find new dynamic modules. + auto &oldModVec = moduleMap[pid]; + if (newModVec.size() <= oldModVec.size()) { + pcerr::New(SUCCESS); + moduleSafeHandler.releaseLock(pid); + return SUCCESS; + } + auto diffModVec = FindDiffMaps(oldModVec, newModVec); + // Load modules. + for (auto& item : diffModVec) { + this->RecordElf(item->moduleName.c_str()); + this->RecordDwarf(item->moduleName.c_str()); + } + for (auto mod : diffModVec) { + oldModVec.push_back(mod); + } + pcerr::New(SUCCESS); + moduleSafeHandler.releaseLock(pid); + return SUCCESS; +} + +void SymbolResolve::FreeModule(int pid) +{ + if (pid < 0) { + return; + } + moduleSafeHandler.tryLock(pid); + auto it = this->moduleMap.find(pid); + if (it != this->moduleMap.end()) { + this->moduleMap.erase(it); + } + moduleSafeHandler.releaseLock(pid); + return; +} + +struct SortElf { + inline bool operator()(const ElfMap& first, const ElfMap& second) + { + return (first.start < second.start); + } +}; + +int SymbolResolve::RecordElf(const char* fileName) +{ + SetFalse(this->isCleared); + std::string file = fileName; + elfSafeHandler.tryLock(file); + + if (this->elfMap.find(fileName) != this->elfMap.end()) { + pcerr::New(0, "success"); + elfSafeHandler.releaseLock(file); + return 0; + } + + if (!SymbolUtils::IsFile(fileName)) { + pcerr::New(LIBSYM_ERR_FILE_NOT_RGE, "libsym detects that the input parameter fileName is not a file"); + elfSafeHandler.releaseLock(file); + return LIBSYM_ERR_FILE_NOT_RGE; + } + + /** symbol cache logic should be implemented after this line */ + int fd = open(fileName, O_RDONLY); + if (fd < 0) { + pcerr::New(LIBSYM_ERR_OPEN_FILE_FAILED, + "libsym can't open file named " + file + " because of " + std::string{strerror(errno)}); + elfSafeHandler.releaseLock(file); + return LIBSYM_ERR_OPEN_FILE_FAILED; + } + + std::shared_ptr efLoader = elf::create_mmap_loader(fd); + elf::elf ef(efLoader); + std::vector elfVector; + + try { + for (const auto& sec : ef.sections()) { + if (sec.get_hdr().type != elf::sht::symtab && sec.get_hdr().type != elf::sht::dynsym) { + continue; + } + ElfInfoRecord(elfVector, sec); + } + } catch (elf::format_error& error) { + pcerr::New(LIBSYM_ERR_ELFIN_FOMAT_FAILED, "libsym record elf format error: " + std::string{error.what()}); + elfSafeHandler.releaseLock(file); + return LIBSYM_ERR_ELFIN_FOMAT_FAILED; + } + + std::sort(elfVector.begin(), elfVector.end(), SortElf()); + this->elfMap.insert({file, elfVector}); + close(fd); + pcerr::New(0, "success"); + elfSafeHandler.releaseLock(file); + return 0; +} + +int SymbolResolve::RecordDwarf(const char* fileName) +{ + SetFalse(this->isCleared); + std::string file = fileName; + dwarfSafeHandler.tryLock(file); + + if (this->dwarfMap.find(fileName) != this->dwarfMap.end()) { + pcerr::New(0, "success"); + dwarfSafeHandler.releaseLock((file)); + return 0; + } + + if (!SymbolUtils::IsFile(fileName)) { + pcerr::New(LIBSYM_ERR_FILE_NOT_RGE, "libsym detects that the input parameter fileName is not a file"); + dwarfSafeHandler.releaseLock((file)); + return LIBSYM_ERR_FILE_NOT_RGE; + } + + int fd = open(fileName, O_RDONLY); + if (fd < 0) { + pcerr::New(LIBSYM_ERR_OPEN_FILE_FAILED, + "libsym can't open file named " + file + " because of " + std::string{strerror(errno)}); + dwarfSafeHandler.releaseLock((file)); + return LIBSYM_ERR_OPEN_FILE_FAILED; + } + /** symbol cache logic should be implemented after this line */ + + auto efLoader = elf::create_mmap_loader(fd); + elf::elf ef(std::move(efLoader)); + + try { + dwarf::dwarf dw(dwarf::elf::create_loader(ef)); + DWARF_DATA_MAP dwarfTable; + for (const auto& cu : dw.compilation_units()) { + const dwarf::line_table lt = cu.get_line_table(); + DwarfInfoRecord(dwarfTable, lt, dwarfFileArray); + } + std::vector addrVet; + for (auto it = dwarfTable.begin(); it != dwarfTable.end(); ++it) { + addrVet.push_back(it->first); + } + this->dwarfMap.insert({file, dwarfTable}); + this->dwarfVetMap.insert({file, addrVet}); + } catch (dwarf::format_error& error) { + dwarfSafeHandler.releaseLock((file)); + pcerr::New(LIBSYM_ERR_DWARF_FORMAT_FAILED, + "libsym record dwarf file named " + file + " format error: " + std::string{error.what()}); + return LIBSYM_ERR_DWARF_FORMAT_FAILED; + } + + efLoader.reset(); + close(fd); + pcerr::New(0, "success"); + dwarfSafeHandler.releaseLock((file)); + return 0; +} + +void SymbolResolve::Clear() +{ + std::lock_guard lock(mutex); + if (!this->instance) { + return; + } + /** + * free the memory allocated for symbol table + */ + FreeSymMap(this->symbolMap); + + /** + * free the memory allocated for stack table + */ + FreeStackMap(this->stackMap); + /** + * free the memory allocated from kernel + */ + FreeKernelVet(this->ksymArray); + this->isCleared = true; + delete this->instance; + this->instance = nullptr; +} + +void SymbolResolve::SearchElfInfo( + std::vector& elfVec, unsigned long addr, struct Symbol* symbol, unsigned long* offset) +{ + ssize_t start = 0; + ssize_t end = elfVec.size() - 1; + ssize_t mid; + unsigned long symAddr; + + while (start < end) { + mid = start + (end - start + 1) / BINARY_HALF; + symAddr = elfVec[mid].start; + if (symAddr <= addr) { + start = mid; + } else { + end = mid - 1; + } + } + + if (start == end && elfVec[start].start <= addr && elfVec[start].end >= addr) { + *offset = addr - elfVec[start].start; + symbol->codeMapEndAddr = elfVec[start].end; + char* name = CppNameDemangle(elfVec[start].symbolName.c_str()); + if (name) { + if (strcpy_s(symbol->symbolName, MAX_LINUX_MODULE_LEN, name) != EOK) { + delete[] symbol->symbolName; + symbol->symbolName = nullptr; + } + free(name); + name = nullptr; + return; + } + if (strcpy_s(symbol->symbolName, MAX_LINUX_MODULE_LEN, elfVec[start].symbolName.c_str()) != EOK) { + delete[] symbol->symbolName; + symbol->symbolName = nullptr; + } + return; + } + if (strcpy_s(symbol->symbolName, MAX_LINUX_MODULE_LEN, "UNKNOWN") != EOK) { + delete[] symbol->symbolName; + symbol->symbolName = nullptr; + } + return; +} + +void SymbolResolve::SearchDwarfInfo( + std::vector& addrVet, DWARF_DATA_MAP& dwarfDataMap, unsigned long addr, struct Symbol* symbol) +{ + DwarfMap dwarfMap = {0}; + bool findLine = false; + if (dwarfDataMap.find(addr) != dwarfDataMap.end()) { + dwarfMap = dwarfDataMap.at(addr); + findLine = true; + } else { + auto it = std::upper_bound(addrVet.begin(), addrVet.end(), addr); + if (it > addrVet.begin() && it < addrVet.end()) { + --it; + } + if (it != addrVet.end()) { + dwarfMap = dwarfDataMap.at(*it); + findLine = true; + } + } + if (findLine) { + std::string fileName = dwarfFileArray.GetKeyByIndex(dwarfMap.fileIndex); + if (strcpy_s(symbol->fileName, MAX_LINUX_MODULE_LEN, fileName.c_str()) != EOK) { + delete[] symbol->fileName; + symbol->fileName = nullptr; + } + symbol->lineNum = dwarfMap.lineNum; + } else { + if (strcpy_s(symbol->fileName, MAX_LINUX_MODULE_LEN, "Uknown") != EOK) { + delete[] symbol->fileName; + symbol->fileName = nullptr; + } + symbol->lineNum = 0; + } +} + +std::shared_ptr SymbolResolve::AddrToModule( + std::vector>& processModule, unsigned long addr) +{ + ssize_t start = 0; + ssize_t end = processModule.size() - 1; + ssize_t mid; + unsigned long symAddr; + + while (start < end) { + mid = start + (end - start + 1) / BINARY_HALF; + symAddr = processModule[mid]->start; + if (symAddr <= addr) { + start = mid; + } else { + end = mid - 1; + } + } + + if (start == end && processModule[start]->start <= addr) { + return processModule[start]; + } + + pcerr::New(LIBSYM_ERR_MAP_ADDR_MODULE_FAILED, "libsym addr can't find module"); + return nullptr; +} + +struct Stack* SymbolResolve::StackToHash(int pid, unsigned long* stack, int nr) +{ + if (this->stackMap.find(pid) == this->stackMap.end()) { + this->stackMap[pid] = {}; + } + size_t stackId = HashStr(stack, nr); + if (this->stackMap.at(pid).find(stackId) != this->stackMap.at(pid).end()) { + return this->stackMap.at(pid).at(stackId); + } + + struct Stack* head = nullptr; + for (int i = nr - 1; i >= 0; i--) { + struct Stack* current = CreateNode(); + auto symbol = this->MapAddr(pid, stack[i]); + if (symbol != nullptr) { + current->symbol = symbol; + } else { + current->symbol = nullptr; + } + AddTail(&head, ¤t); + } + + if (this->stackMap.at(pid).find(stackId) == this->stackMap.at(pid).end()) { + this->stackMap.at(pid)[stackId] = head; + } + pcerr::New(0, "success"); + return head; +} + +struct Symbol* SymbolResolve::MapKernelAddr(unsigned long addr) +{ + ssize_t start = 0; + ssize_t end = this->ksymArray.size() - 1; + ssize_t mid; + __u64 symAddr; + + while (start < end) { + mid = start + (end - start + 1) / BINARY_HALF; + symAddr = this->ksymArray[mid]->addr; + if (symAddr <= addr) { + start = mid; + } else { + end = mid - 1; + } + } + + if (start == end && this->ksymArray[start]->addr <= addr) { + return this->ksymArray[start].get(); + } + pcerr::New(LIBSYM_ERR_MAP_KERNAL_ADDR_FAILED, "libsym cannot find the corresponding kernel address"); + return nullptr; +} + +struct Symbol* SymbolResolve::MapUserAddr(int pid, unsigned long addr) +{ + if (this->moduleMap.find(pid) == this->moduleMap.end()) { + pcerr::New(LIBSYM_ERR_NOT_FIND_PID, "The libsym process ID " + std::to_string(pid) + " cannot be found."); + return nullptr; + } + + std::shared_ptr module = this->AddrToModule(this->moduleMap.at(pid), addr); + if (!module) { + return nullptr; + } + /** + * Try to search elf data first + */ + symSafeHandler.tryLock(pid); + if (this->symbolMap.find(pid) == this->symbolMap.end()) { + this->symbolMap[pid] = {}; + } + symSafeHandler.releaseLock(pid); + auto it = this->symbolMap.at(pid).find(addr); + if (it != this->symbolMap.at(pid).end()) { + return it->second; + } + struct Symbol* symbol = new struct Symbol(); + symbol->module = InitChar(MAX_LINUX_MODULE_LEN); + symbol->symbolName = InitChar(MAX_LINUX_SYMBOL_LEN); + symbol->fileName = InitChar(MAX_LINUX_FILE_NAME); + symbol->addr = addr; + symbol->offset = 0; + if (strcpy_s(symbol->module, MAX_LINUX_MODULE_LEN, module->moduleName.c_str()) != EOK) { + delete[] symbol->module; + symbol->module = nullptr; + } + unsigned long addrToSearch = addr; + if (this->elfMap.find(module->moduleName) != this->elfMap.end()) { + // If the largest symbol in the elf symbol table is detected to be smaller than the searched symbol, subtraction + // is performed. + std::vector elfVet = this->elfMap.at(module->moduleName); + if (!elfVet.empty()) { + if (elfVet.back().end < addrToSearch && addrToSearch > module->start) { + addrToSearch = addrToSearch - module->start; + } + this->SearchElfInfo(elfVet, addrToSearch, symbol, &symbol->offset); + } + } + + if (this->dwarfMap.find(module->moduleName) != this->dwarfMap.end()) { + this->SearchDwarfInfo( + this->dwarfVetMap.at(module->moduleName), this->dwarfMap.at(module->moduleName), addrToSearch, symbol); + } + symbol->codeMapAddr = addrToSearch; + this->symbolMap.at(pid).insert({addr, symbol}); + pcerr::New(0, "success"); + return symbol; +} + +struct Symbol* SymbolResolve::MapAddr(int pid, unsigned long addr) +{ + struct Symbol* data = nullptr; + if (addr > KERNEL_START_ADDR) { + data = this->MapKernelAddr(addr); + if (data == nullptr) { + return nullptr; + } + data->offset = addr - data->addr; + } else { + data = this->MapUserAddr(pid, addr); + } + return data; +} + +int SymbolResolve::RecordKernel() +{ + SetFalse(this->isCleared); + if (!this->ksymArray.empty()) { + pcerr::New(0, "success"); + return 0; + } + // Prevent multiple threads from processing kernel data at the same time. + std::lock_guard guard(kernelMutex); + + FILE* kallsyms = fopen("/proc/kallsyms", "r"); + if (__glibc_unlikely(kallsyms == nullptr)) { + pcerr::New(LIBSYM_ERR_KALLSYMS_INVALID, + "libsym failed to open /proc/kallsyms, found that file /proc/kallsyms " + std::string{strerror(errno)}); + return LIBSYM_ERR_KALLSYMS_INVALID; + } + + char line[MAX_LINE_LENGTH]; + char mode; + __u64 addr; + char name[KERNEL_MODULE_LNE]; + + while (fgets(line, sizeof(line), kallsyms)) { + if (!sscanf_s(line, "%llx %c %s%*[^\n]\n", &addr, &mode, 1, name, KERNEL_MODULE_LNE)) { + continue; + } + ssize_t nameLen = strlen(name); + std::shared_ptr data = std::make_shared(); + data->symbolName = InitChar(nameLen); + if (strcpy_s(data->symbolName, MAX_LINUX_MODULE_LEN, name) != EOK) { + delete[] data->symbolName; + data->symbolName = nullptr; + } + data->addr = addr; + data->fileName = InitChar(KERNEL_NAME_LEN); + if (strcpy_s(data->fileName, MAX_LINUX_MODULE_LEN, "KERNEL") != EOK) { + delete[] data->fileName; + data->fileName = nullptr; + } + data->module = InitChar(KERNEL_NAME_LEN); + if (strcpy_s(data->module, MAX_LINUX_MODULE_LEN, "KERNEL") != EOK) { + delete[] data->module; + data->module = nullptr; + } + data->lineNum = 0; + this->ksymArray.emplace_back(data); + } + + fclose(kallsyms); + pcerr::New(0, "success"); + return 0; +} + +int SymbolResolve::UpdateModule(int pid, const char* moduleName, unsigned long startAddr) +{ + if (pid < 0) { + pcerr::New(LIBSYM_ERR_PARAM_PID_INVALID, "libsym param process ID must be greater than 0"); + return LIBSYM_ERR_PARAM_PID_INVALID; + } + SetFalse(this->isCleared); + std::shared_ptr data = std::make_shared(); + data->moduleName = moduleName; + data->start = startAddr; + int ret = this->RecordElf(data->moduleName.c_str()); + if (ret == LIBSYM_ERR_OPEN_FILE_FAILED || ret == LIBSYM_ERR_FILE_NOT_RGE) { + return ret; + } + + this->RecordDwarf(data->moduleName.c_str()); + if (ret == LIBSYM_ERR_OPEN_FILE_FAILED || ret == LIBSYM_ERR_FILE_NOT_RGE) { + return ret; + } + + if (this->moduleMap.find(pid) == this->moduleMap.end()) { + std::vector> modVec; + modVec.emplace_back(data); + this->moduleMap[pid] = modVec; + } else { + this->moduleMap[pid].emplace_back(data); + } + pcerr::New(0, "success"); + return 0; +} + +struct Symbol* SymbolResolve::MapCodeElfAddr(const std::string& moduleName, unsigned long addr) +{ + struct Symbol* data = nullptr; + if (addr > KERNEL_START_ADDR) { + pcerr::New(LIBSYM_ERR_MAP_CODE_KERNEL_NOT_SUPPORT, + "libsym The current version does not support kernel source code matching."); + return nullptr; + } else { + int ret = RecordElf(moduleName.c_str()); + if (ret != 0) { + return nullptr; + } + data = this->MapUserCodeAddr(moduleName, addr); + } + return data; +} + +struct Symbol* SymbolResolve::MapUserCodeAddr(const std::string& moduleName, unsigned long startAddr) +{ + struct Symbol* symbol = new Symbol(); + symbol->symbolName = InitChar(MAX_LINUX_SYMBOL_LEN); + symbol->fileName = InitChar(MAX_LINUX_SYMBOL_LEN); + symbol->module = InitChar(MAX_LINUX_MODULE_LEN); + symbol->addr = startAddr; + unsigned long addrToSearch = startAddr; + symbol->codeMapAddr = addrToSearch; + if (strcpy_s(symbol->module, MAX_LINUX_MODULE_LEN, moduleName.c_str()) != EOK) { + delete[] symbol->module; + symbol->module = nullptr; + } + if (this->elfMap.find(moduleName) != this->elfMap.end()) { + this->SearchElfInfo(this->elfMap.at(moduleName), addrToSearch, symbol, &symbol->offset); + } + pcerr::New(0, "success"); + return symbol; +} + +struct StackAsm* SymbolResolve::MapAsmCode(const char* moduleName, unsigned long startAddr, unsigned long endAddr) +{ + struct StackAsm* stackAsm = MapAsmCodeStack(moduleName, startAddr, endAddr); + return stackAsm; +} + +struct Symbol* SymbolResolve::MapCodeAddr(const char* moduleName, unsigned long startAddr) +{ + Symbol* symbol = MapCodeElfAddr(moduleName, startAddr); + if (!symbol) { + return nullptr; + } + int ret = RecordDwarf(moduleName); + if (ret == 0) { + this->SearchDwarfInfo( + this->dwarfVetMap.at(moduleName), this->dwarfMap.at(moduleName), symbol->codeMapAddr, symbol); + } else { + symbol->fileName = nullptr; + } + return symbol; +} + +struct StackAsm* ReadAsmCodeFromPipe(FILE* pipe) +{ + struct StackAsm* head = nullptr; + struct StackAsm* last = nullptr; + int lineLen = MAX_LINE_LENGTH; + char line[lineLen]; + std::string srcFileName = "unknown"; + unsigned int srcLineNum = 0; + while (!feof(pipe)) { + memset_s(line, lineLen, 0, lineLen); + if (!fgets(line, lineLen, pipe)) { + break; + } + if (!line || (line && line[0] == '\0') || (line && line[0] == '\n')) { + continue; + } + struct StackAsm* stackAsm = nullptr; + if (strstr(line, "File Offset") != nullptr) { + stackAsm = FirstLineMatch(line); + } + if (!stackAsm && head) { + if (!CheckColonSuffix(line)) { + continue; + } + if (MatchFileName(line, srcFileName, srcLineNum)) { + continue; + } + AsmCode* asmCode = MatchAsmCode(line, srcFileName, srcLineNum); + if (!asmCode) { + continue; + } + struct StackAsm* current = CreateNode(); + current->funcName = nullptr; + current->asmCode = asmCode; + last->next = current; + last = current; + continue; + } + if (!head && stackAsm) { + AddTail(&head, &stackAsm); + last = head; + } + if (stackAsm && head) { + last->next = stackAsm; + last = stackAsm; + } + } + pclose(pipe); + pipe = nullptr; + return head; +} + +struct StackAsm* SymbolResolve::MapAsmCodeStack( + const std::string& moduleName, unsigned long startAddr, unsigned long endAddr) +{ + char startAddrStr[ADDR_LEN]; + char endAddrStr[ADDR_LEN]; + if (startAddr >= endAddr) { + pcerr::New(LIBSYM_ERR_START_SMALLER_END, "libysm the end address must be greater than the start address"); + return nullptr; + } + if (!snprintf_s(startAddrStr, ADDR_LEN + 1, ADDR_LEN, "0x%lx", startAddr)) { + pcerr::New(LIBSYM_ERR_SNPRINF_OPERATE_FAILED, "libsym fails to execute snprintf"); + return nullptr; + } + + if (!snprintf_s(endAddrStr, ADDR_LEN + 1, ADDR_LEN, "0x%lx", endAddr)) { + pcerr::New(LIBSYM_ERR_SNPRINF_OPERATE_FAILED, "libsym fails to execute snprintf"); + return nullptr; + } + std::string cmd = "objdump -Fld " + moduleName + " --start-address=" + std::string{startAddrStr} + + " --stop-address=" + std::string{endAddrStr}; + FILE* pipe = popen(cmd.c_str(), "r"); + if (!pipe) { + pcerr::New(LIBSYM_ERR_CMD_OPERATE_FAILED, + "libsym fails to obtain the assembly instruction" + std::string{strerror(errno)}); + return nullptr; + } + struct StackAsm* head = ReadAsmCodeFromPipe(pipe); + pcerr::New(0, "success"); + return head; +} + +std::vector> SymbolResolve::FindDiffMaps( + const std::vector>& oldMaps, + const std::vector>& newMaps) const +{ + std::vector> diffMaps; + for (auto newMod : newMaps) { + for (auto oldMod : oldMaps) { + if (newMod->start != oldMod->start) { + diffMaps.push_back(newMod); + } + } + } + + return diffMaps; +} + +SymbolResolve* SymbolResolve::instance = nullptr; +std::mutex SymbolResolve::mutex; +std::mutex SymbolResolve::kernelMutex; \ No newline at end of file diff --git a/symbol/symbol_resolve.h b/symbol/symbol_resolve.h new file mode 100644 index 0000000..e87d6ba --- /dev/null +++ b/symbol/symbol_resolve.h @@ -0,0 +1,167 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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-04-03 + * Description: Provide a complete set of symbolic analysis tools, perform operations such as + * module records, address analysis and stack conversion. + ******************************************************************************/ +#ifndef USER_SYMBOL_H +#define USER_SYMBOL_H +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "safe_handler.h" +#include "linked_list.h" +#include "symbol.h" + +namespace KUNPENG_SYM { + struct ModuleMap { + unsigned long start; + unsigned long end; + std::string moduleName; + } __attribute__((aligned(8))); + + struct ElfMap { + unsigned long start; + unsigned long end; + std::string symbolName; + } __attribute__((aligned(8))); + + struct DwarfMap { + unsigned int lineNum; + unsigned int fileIndex; + } __attribute__((aligned(8))); + + enum class RecordModuleType { RECORD_ALL = 0, RECORD_NO_DWARF = 1 }; + + using SYMBOL_MAP = std::unordered_map>; + using STACK_MAP = std::unordered_map>; + using MODULE_MAP = std::unordered_map>>; + using DWARF_DATA_MAP = std::map; + using DWARF_MAP = std::unordered_map; + using DWARF_VET_MAP = std::unordered_map>; + using ELF_MAP = std::unordered_map>; + + template + class SymbolVet : public std::vector { + public: + unsigned int InsertKeyForIndex(const Key& key) + { + std::lock_guard guard(keyMutex); + if (keyMap.find(key) != keyMap.end()) { + return keyMap.at(key); + } + this->push_back(key); + keyMap[key] = this->size() - 1; + return this->size() - 1; + } + + Key& GetKeyByIndex(const unsigned int index) + { + std::lock_guard guard(keyMutex); + if (index < this->size()) { + return this->at(index); + } + Key key = {}; + return key; + } + + private: + std::unordered_map keyMap; + std::mutex keyMutex; + }; + + class SymbolUtils final { + public: + SymbolUtils() = default; + ~SymbolUtils() = default; + static void FreeSymbol(struct Symbol* symbol); + static bool IsFile(const char* fileName); + static unsigned long SymStoul(const std::string& addrStr); + static std::string RealPath(const std::string& filePath); + static bool IsValidPath(const std::string& filePath); + static bool IsNumber(const std::string& str); + static void FreeStackAsm(struct StackAsm** stackAsm); + }; + class SymbolResolve { + public: + static SymbolResolve* GetInstance() + { + // Double-checked locking for thread safety + if (instance == nullptr) { + std::lock_guard lock(mutex); + if (instance == nullptr) { + instance = new SymbolResolve(); + } + } + return instance; + } + + int RecordModule(int pid, RecordModuleType recordModuleType); + void FreeModule(int pid); + int RecordKernel(); + int RecordElf(const char* fileName); + int RecordDwarf(const char* fileName); + int UpdateModule(int pid); + int UpdateModule(int pid, const char* moduleName, unsigned long startAddr); + void Clear(); + std::shared_ptr AddrToModule(std::vector>& processModule, unsigned long addr); + struct Stack* StackToHash(int pid, unsigned long* stack, int nr); + struct Symbol* MapAddr(int pid, unsigned long addr); + struct StackAsm* MapAsmCode(const char* moduleName, unsigned long startAddr, unsigned long endAddr); + struct Symbol* MapCodeAddr(const char* moduleName, unsigned long startAddr); + + private: + void SearchElfInfo( + std::vector& elfVec, unsigned long addr, struct Symbol* symbol, unsigned long* offset); + void SearchDwarfInfo( + std::vector& addrVet, DWARF_DATA_MAP& dwalfVec, unsigned long addr, struct Symbol* symbol); + struct Symbol* MapKernelAddr(unsigned long addr); + struct Symbol* MapUserAddr(int pid, unsigned long addr); + struct Symbol* MapUserCodeAddr(const std::string& moduleName, unsigned long addr); + struct Symbol* MapCodeElfAddr(const std::string& moduleName, unsigned long addr); + struct StackAsm* MapAsmCodeStack(const std::string& moduleName, unsigned long startAddr, unsigned long endAddr); + std::vector> FindDiffMaps(const std::vector>& oldMaps, + const std::vector>& newMaps) const; + + SYMBOL_MAP symbolMap{}; + STACK_MAP stackMap{}; + MODULE_MAP moduleMap{}; + DWARF_MAP dwarfMap{}; + DWARF_VET_MAP dwarfVetMap{}; + ELF_MAP elfMap{}; + bool isCleared = false; + std::vector> ksymArray; + SymbolVet dwarfFileArray; + SymbolResolve() + {} + + SymbolResolve(const SymbolResolve&) = delete; + SymbolResolve& operator=(const SymbolResolve&) = delete; + + ~SymbolResolve() + {} + SafeHandler moduleSafeHandler; + SafeHandler dwarfSafeHandler; + SafeHandler elfSafeHandler; + SafeHandler symSafeHandler; + static std::mutex kernelMutex; + static SymbolResolve* instance; + static std::mutex mutex; + }; +} // namespace KUNPENG_SYM +#endif \ No newline at end of file -- Gitee From d127a3d75453e10aec5787f0b1e0822d30b330c8 Mon Sep 17 00:00:00 2001 From: Junyi Ye <294572668@qq.com> Date: Tue, 9 Apr 2024 15:23:52 +0800 Subject: [PATCH 06/14] pfm supports uncore event query --- pmu/pfm/pfm.cpp | 152 ++++++-------------------------------------- pmu/pfm/pfm_event.h | 5 +- pmu/pfm/trace.cpp | 2 +- pmu/pfm/trace.h | 20 ++++++ pmu/pfm/uncore.cpp | 109 +++++++++++++++++++++++++++++++ pmu/pfm/uncore.h | 15 ++--- 6 files changed, 156 insertions(+), 147 deletions(-) create mode 100644 pmu/pfm/trace.h create mode 100644 pmu/pfm/uncore.cpp diff --git a/pmu/pfm/pfm.cpp b/pmu/pfm/pfm.cpp index 07f9ec1..99e9b98 100644 --- a/pmu/pfm/pfm.cpp +++ b/pmu/pfm/pfm.cpp @@ -47,17 +47,7 @@ static struct PmuEvt* ConstructPmuEvtFromCore(KUNPENG_PMU::CoreConfig config, in pmuEvtPtr->config = config.config; pmuEvtPtr->name = config.eventName; pmuEvtPtr->type = config.type; - pmuEvtPtr->collectType = collectType; - return std::move(pmuEvtPtr); -} - -static struct PmuEvt* ConstructPmuEvtFromUncore(KUNPENG_PMU::UncoreConfig config, int collectType) -{ - struct PmuEvt* pmuEvtPtr = new PmuEvt; - pmuEvtPtr->config = config.config; - pmuEvtPtr->name = config.eventName; - pmuEvtPtr->type = config.type; - pmuEvtPtr->pmuType = config.pmuType; + pmuEvtPtr->pmuType = CORE_TYPE; pmuEvtPtr->collectType = collectType; return std::move(pmuEvtPtr); } @@ -71,35 +61,23 @@ static struct PmuEvt* GetCoreEvent(const char* pmuName, int collectType) : nullptr; } -static struct PmuEvt* GetUncoreDDRCEvent(const char* pmuName, int collectType) +static struct PmuEvt* GetUncoreEvent(const char* pmuName, int collectType) { - return KUNPENG_PMU::DDRC_EVENT_MAP.at(g_chipType).find(pmuName) != - KUNPENG_PMU::DDRC_EVENT_MAP.at(g_chipType).end() - ? ConstructPmuEvtFromUncore(KUNPENG_PMU::DDRC_EVENT_MAP.at(g_chipType).at(pmuName), collectType) - : nullptr; -} - -static struct PmuEvt* GetUncoreHHAEvent(const char* pmuName, int collectType) -{ - return KUNPENG_PMU::HHA_EVENT_MAP.at(g_chipType).find(pmuName) != - KUNPENG_PMU::HHA_EVENT_MAP.at(g_chipType).end() - ? ConstructPmuEvtFromUncore( - KUNPENG_PMU::HHA_EVENT_MAP.at(g_chipType).at(pmuName), collectType) - : nullptr; -} - -static struct PmuEvt* GetUncoreLLCEvent(const char* pmuName, int collectType) -{ - return KUNPENG_PMU::L3C_EVENT_MAP.at(g_chipType).find(pmuName) != - KUNPENG_PMU::L3C_EVENT_MAP.at(g_chipType).end() - ? ConstructPmuEvtFromUncore( - KUNPENG_PMU::L3C_EVENT_MAP.at(g_chipType).at(pmuName), collectType) - : nullptr; + int64_t config = GetUncoreEventConfig(pmuName); + if (config == -1) { + return nullptr; + } + auto* pmuEvtPtr = new PmuEvt; + pmuEvtPtr->config = config; + pmuEvtPtr->name = pmuName; + pmuEvtPtr->pmuType = UNCORE_TYPE; + pmuEvtPtr->collectType = collectType; + return pmuEvtPtr; } static struct PmuEvt* GetKernelTraceEvent(const char* pmuName, int collectType) { - int64_t config = GetTraceEventID(pmuName); + int64_t config = GetTraceEventConfig(pmuName); if (config == -1) { return nullptr; } @@ -138,13 +116,11 @@ using EvtRetriever = std::function; static const std::unordered_map EvtMap{ {KUNPENG_PMU::CORE_TYPE, GetCoreEvent}, - {KUNPENG_PMU::HHA_TYPE, GetUncoreHHAEvent}, - {KUNPENG_PMU::L3C_TYPE, GetUncoreLLCEvent}, - {KUNPENG_PMU::DDRC_TYPE, GetUncoreDDRCEvent}, + {KUNPENG_PMU::UNCORE_TYPE, GetUncoreEvent}, {KUNPENG_PMU::TRACE_TYPE, GetKernelTraceEvent}, }; -static int GetEventType(const char *pmuName, string &evtName, string &devName) +static int GetEventType(const char *pmuName, string &evtName) { auto coreMap = CORE_EVENT_MAP.at(g_chipType); auto findCoreEvent = coreMap.find(pmuName); @@ -165,99 +141,12 @@ static int GetEventType(const char *pmuName, string &evtName, string &devName) if (findSlash == string::npos) { return -1; } - devName = strName.substr(0, findSlash); findSlash = strName.find('/', findSlash); if (findSlash == string::npos) { return -1; } - evtName = strName.substr(devName.size() + 1, strName.size() - 1 - (devName.size() + 1)); - auto ddrMap = DDRC_EVENT_MAP.at(g_chipType); - auto findDDrEvent = ddrMap.find(evtName); - if (findDDrEvent != ddrMap.end()) { - return DDRC_TYPE; - } - - return -1; -} - -static int GetDeviceType(const string &devName) -{ - string typePath = "/sys/devices/" + 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 int GetCpuMask(const string &devName) -{ - string maskPath = "/sys/devices/" + devName + "/cpumask"; - std::string realPath = GetRealPath(maskPath); - if (!IsValidPath(realPath)) { - return -1; - } - ifstream maskIn(realPath); - if (!maskIn.is_open()) { - return -1; - } - // Cpumask is a comma-separated list of integers, - // but now make it simple for ddrc event. - string maskStr; - maskIn >> maskStr; - - return stoi(maskStr); -} - -static bool GetEvtConfig(const string &devName, const char* pmuName, const string &evtName, __u64 &config) -{ - string evtPath = "/sys/devices/" + devName + "/events/" + evtName; - std::string realPath = GetRealPath(evtPath); - if (!IsValidPath(realPath)) { - return false; - } - ifstream evtIn(realPath); - if (!evtIn.is_open()) { - return false; - } - string configStr; - evtIn >> configStr; - auto findEq = configStr.find("="); - if (findEq == string::npos) { - return false; - } - auto subStr = configStr.substr(findEq + 1, configStr.size() - findEq); - std::istringstream iss(subStr); - iss >> std::hex >> config; - - return true; -} - -static int FillUncoreFields(const string &devName, const char* pmuName, const string &evtName, PmuEvt *evt) -{ - int devType = GetDeviceType(devName); - if (devType == -1) { - return UNKNOWN_ERROR; - } - evt->type = devType; - int cpuMask = GetCpuMask(devName); - if (cpuMask == -1) { - return UNKNOWN_ERROR; - } - if (!GetEvtConfig(devName, pmuName, evtName, evt->config)) { - return LIBPERF_ERR_INVALID_EVENT; - } - - evt->cpumask = cpuMask; - evt->name = pmuName; - return SUCCESS; + evtName = pmuName; + return UNCORE_TYPE; } struct PmuEvt* PfmGetPmuEvent(const char* pmuName, int collectType) @@ -268,12 +157,11 @@ struct PmuEvt* PfmGetPmuEvent(const char* pmuName, int collectType) return evt; } string evtName; - string devName; g_chipType = GetCpuType(); if (g_chipType == UNDEFINED_TYPE) { return nullptr; } - auto type = GetEventType(pmuName, evtName, devName); + auto type = GetEventType(pmuName, evtName); if (type == -1) { return nullptr; } @@ -282,9 +170,9 @@ struct PmuEvt* PfmGetPmuEvent(const char* pmuName, int collectType) if (evt == nullptr) { return evt; } - if (!devName.empty()) { + if (evt->pmuType == UNCORE_TYPE) { // Fill fields for uncore devices. - auto err = FillUncoreFields(devName, pmuName, evtName, evt); + auto err = FillUncoreFields(pmuName, evt); if (err != SUCCESS) { return nullptr; } diff --git a/pmu/pfm/pfm_event.h b/pmu/pfm/pfm_event.h index 0112089..3879c43 100644 --- a/pmu/pfm/pfm_event.h +++ b/pmu/pfm/pfm_event.h @@ -23,10 +23,7 @@ namespace KUNPENG_PMU { enum PMU_TYPE { CORE_TYPE, - DDRC_TYPE, - HHA_TYPE, - L3C_TYPE, - PCIE_TYPE, + UNCORE_TYPE, TRACE_TYPE, }; diff --git a/pmu/pfm/trace.cpp b/pmu/pfm/trace.cpp index b13ce19..a4e7b87 100644 --- a/pmu/pfm/trace.cpp +++ b/pmu/pfm/trace.cpp @@ -18,7 +18,7 @@ using namespace std; -int64_t GetTraceEventID(const std::string &name) +int64_t GetTraceEventConfig(const std::string &name) { size_t colon = name.find(':'); string systemName = name.substr(0, colon); diff --git a/pmu/pfm/trace.h b/pmu/pfm/trace.h new file mode 100644 index 0000000..eaa2d1e --- /dev/null +++ b/pmu/pfm/trace.h @@ -0,0 +1,20 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: trace point event query interface + ******************************************************************************/ +#ifndef TRACE_H +#define TRACE_H + +int64_t GetTraceEventConfig(const std::string &name); + +#endif diff --git a/pmu/pfm/uncore.cpp b/pmu/pfm/uncore.cpp new file mode 100644 index 0000000..3db4a17 --- /dev/null +++ b/pmu/pfm/uncore.cpp @@ -0,0 +1,109 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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 +#include "common.h" +#include "pcerr.h" +#include "uncore.h" + +using namespace std; + +static int GetDeviceType(const string &devName) +{ + string typePath = "/sys/devices/" + 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 int GetCpuMask(const string &devName) +{ + string maskPath = "/sys/devices/" + devName + "/cpumask"; + std::string realPath = GetRealPath(maskPath); + if (!IsValidPath(realPath)) { + return -1; + } + ifstream maskIn(realPath); + if (!maskIn.is_open()) { + return -1; + } + // Cpumask is a comma-separated list of integers, + // but now make it simple for ddrc event. + string maskStr; + maskIn >> maskStr; + + return stoi(maskStr); +} + +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/devices/" + 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; + } + 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; + int cpuMask = GetCpuMask(devName); + if (cpuMask == -1) { + return UNKNOWN_ERROR; + } + if (GetUncoreEventConfig(pmuName) == -1) { + return LIBPERF_ERR_INVALID_EVENT; + } + + evt->cpumask = cpuMask; + evt->name = pmuName; + return SUCCESS; +} \ No newline at end of file diff --git a/pmu/pfm/uncore.h b/pmu/pfm/uncore.h index 002e8d5..cab7388 100644 --- a/pmu/pfm/uncore.h +++ b/pmu/pfm/uncore.h @@ -10,19 +10,14 @@ * See the Mulan PSL v2 for more details. * Author: Mr.Ye * Create: 2024-04-03 - * Description: uncore event map declaration + * Description: uncore event query interface ******************************************************************************/ #ifndef UNCORE_H #define UNCORE_H -#include -#include "pfm_event.h" -#include "pfm_name.h" +#include "pmu_event.h" -namespace KUNPENG_PMU { - extern const KUNPENG_PMU::UNCORE_EVT_MAP L3C_EVENT_MAP; - extern const KUNPENG_PMU::UNCORE_EVT_MAP HHA_EVENT_MAP; - extern const KUNPENG_PMU::UNCORE_EVT_MAP DDRC_EVENT_MAP; - extern const KUNPENG_PMU::UNCORE_EVT_MAP PCIE_EVENT_MAP; -} // namespace KUNPENG_PMU +int64_t GetUncoreEventConfig(const char* pmuName); + +int FillUncoreFields(const char* pmuName, PmuEvt *evt); #endif \ No newline at end of file -- Gitee From 7d3edcb1c48de341094adb84b91b4af731043738 Mon Sep 17 00:00:00 2001 From: zhangyuhan Date: Tue, 9 Apr 2024 21:53:15 +0800 Subject: [PATCH 07/14] my first commit --- pmu/evt.cpp | 105 ++++++++++ pmu/evt.h | 65 ++++++ pmu/evt_list.cpp | 190 +++++++++++++++++ pmu/evt_list.h | 84 ++++++++ pmu/pmu_event.cpp | 37 ++++ pmu/pmu_event.h | 166 +++++++++++++++ pmu/pmu_list.cpp | 503 ++++++++++++++++++++++++++++++++++++++++++++++ pmu/pmu_list.h | 138 +++++++++++++ 8 files changed, 1288 insertions(+) create mode 100644 pmu/evt.cpp create mode 100644 pmu/evt.h create mode 100644 pmu/evt_list.cpp create mode 100644 pmu/evt_list.h create mode 100644 pmu/pmu_event.cpp create mode 100644 pmu/pmu_event.h create mode 100644 pmu/pmu_list.cpp create mode 100644 pmu/pmu_list.h diff --git a/pmu/evt.cpp b/pmu/evt.cpp new file mode 100644 index 0000000..8d9f17a --- /dev/null +++ b/pmu/evt.cpp @@ -0,0 +1,105 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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 and interacting with performance events using system calls and inline assembly + ******************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include "evt.h" + +enum class HEAD_SIZE { + HEAD_SIZE_ONE = 1, + HEAD_SIZE_TWO = 2, + HEAD_SIZE_FOUR = 4, + HEAD_SIZE_EIGHT = 8, +}; + +static constexpr double CUT_OFF_PERCENT = 0.05; +int KUNPENG_PMU::PerfEventOpen(struct perf_event_attr *attr, pid_t pid, int cpu, int groupFd, unsigned long flags) +{ + return syscall(__NR_perf_event_open, attr, pid, cpu, groupFd, flags); +} + +bool KUNPENG_PMU::PerfEvt::Enable() +{ + return (ioctl(this->fd, PERF_EVENT_IOC_ENABLE, 0) == 0); +} + +bool KUNPENG_PMU::PerfEvt::Reset() +{ + return (ioctl(this->fd, PERF_EVENT_IOC_RESET, 0) == 0); +} + +bool KUNPENG_PMU::PerfEvt::Disable() +{ + return ioctl(this->fd, PERF_EVENT_IOC_DISABLE, 0); +} + +bool KUNPENG_PMU::PerfEvt::Close() +{ + close(this->fd); + return true; +} + +bool KUNPENG_PMU::PerfEvt::Start() +{ + this->Reset(); + return this->Enable(); +} + +bool KUNPENG_PMU::PerfEvt::Pause() +{ + return this->Disable(); +} + +__u64 KUNPENG_PMU::ReadOnce(__u64 *head) +{ + union { + typeof(*head) val; + char charHead[1]; + } pointerUnion = {.charHead = {0}}; + + switch (static_cast(sizeof(*head))) { + case HEAD_SIZE::HEAD_SIZE_ONE: + asm volatile("ldarb %w0, %1" + : "=r"(*(__u8 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); + break; + case HEAD_SIZE::HEAD_SIZE_TWO: + asm volatile("ldarh %w0, %1" + : "=r"(*(__u16 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); + break; + case HEAD_SIZE::HEAD_SIZE_FOUR: + asm volatile("ldar %w0, %1" + : "=r"(*(__u32 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); + break; + case HEAD_SIZE::HEAD_SIZE_EIGHT: + asm volatile("ldar %0, %1" + : "=r"(*(__u64 __attribute__((__may_alias__)) *)pointerUnion.charHead) + : "Q"(*head) + : "memory"); + break; + default: + break; + } + return pointerUnion.val; +} diff --git a/pmu/evt.h b/pmu/evt.h new file mode 100644 index 0000000..bb6fc49 --- /dev/null +++ b/pmu/evt.h @@ -0,0 +1,65 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: declaration of class PerfEvt and related functions for managing performance events in + * the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_EVT_H +#define PMU_EVT_H +#include +#include +#include +#include +#include +#include "symbol.h" +#include "pmu_event.h" + +namespace KUNPENG_PMU { +class PerfEvt { +public: + using ProcPtr = std::shared_ptr; + using ProcMap = std::shared_ptr; + + PerfEvt(int cpu, int pid, struct PmuEvt* evt, ProcMap& procMap) : cpu(cpu), pid(pid), evt(evt), procMap(procMap) + {} + ~PerfEvt() + {} + virtual bool Start(); + virtual bool Pause(); + virtual bool Disable(); + virtual bool Enable(); + virtual bool Reset(); + virtual bool Close(); + + virtual int Init(); + + virtual int Read(std::vector &data, std::vector &sampleIps) = 0; + + virtual int MapPerfAttr() = 0; + + int GetFd() const + { + return fd; + } + +protected: + __u64 const; + int fd; + int cpu; + pid_t pid; + struct PmuEvt* evt; + ProcMap &procMap; +}; +int PerfEventOpen(struct perf_event_attr* attr, pid_t pid, int cpu, int groupFd, unsigned long flags); +__u64 ReadOnce(__u64 *head); +} // namespace KUNPENG_PMU +#endif \ No newline at end of file diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp new file mode 100644 index 0000000..2ad5489 --- /dev/null +++ b/pmu/evt_list.cpp @@ -0,0 +1,190 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: implementations for managing and interacting with performance events in the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include +#include +#include +#include "securec.h" +#include "cpu_map.h" +#include "linked_list.h" +#include "pmu_event.h" +#include "pcerrc.h" +#include "log.h" +#include "evt_list.h" + +using namespace std; + +enum PmuTask { + START = 0, + PAUSE = 1, + DISABLE = 2, + ENABLE = 3, + RESET = 4, + OPEN = 5, + CLOSE = 6, + INIT = 7, + READ = 8, + STOP = 9, +}; + +int KUNPENG_PMU::EvtList::CollectorDoTask(PerfEvtPtr collector, int task) +{ + switch (task) { + case START: + return collector->Start(); + case PAUSE: + return collector->Pause(); + case DISABLE: + return collector->Disable(); + case ENABLE: + return collector->Enable(); + case RESET: + return collector->Reset(); + case CLOSE: { + auto ret = collector->Close(); + if (ret == SUCCESS) { + fdList.erase(collector->GetFd()); + } + return ret; + } + case INIT: + return collector->Init(); + default: + return UNKNOWN_ERROR; + } +} + +int KUNPENG_PMU::EvtList::CollectorXYArrayDoTask( + int cpuCnt, int pidCnt, std::vector>& xyArray, int task) +{ + for (int row = 0; row < cpuCnt; row++) { + for (int col = 0; col < pidCnt; col++) { + if (!CollectorDoTask(xyArray[row][col], task)) { + continue; + } + } + } + return SUCCESS; +} + +int KUNPENG_PMU::EvtList::Init() +{ + // Init process map. + for (auto &proc : pidList) { + if (proc->tid > 0) { + procMap[proc->tid] = proc; + } + } + + for (unsigned int row = 0; row < numCpu; row++) { + std::vector evtVec{}; + for (unsigned int col = 0; col < numPid; col++) { + PerfEvtPtr perfEvt = + this->MapPmuAttr(this->cpuList[row]->coreId, this->pidList[col]->tid, this->pmuEvt.get()); + if (perfEvt == nullptr) { + continue; + } + auto err = perfEvt->Init(); + if (err != SUCCESS) { + return err; + } + fdList.insert(perfEvt->GetFd()); + evtVec.emplace_back(perfEvt); + } + this->xyCounterArray.emplace_back(evtVec); + } + return SUCCESS; +} + +int KUNPENG_PMU::EvtList::Start() +{ + return CollectorXYArrayDoTask(this->numCpu, this->numPid, this->xyCounterArray, START); +} + +int KUNPENG_PMU::EvtList::Enable() +{ + return CollectorXYArrayDoTask(this->numCpu, this->numPid, this->xyCounterArray, ENABLE); +} + +int KUNPENG_PMU::EvtList::Stop() +{ + return CollectorXYArrayDoTask(this->numCpu, this->numPid, this->xyCounterArray, STOP); +} + +int KUNPENG_PMU::EvtList::Close() +{ + auto ret = CollectorXYArrayDoTask(this->numCpu, this->numPid, this->xyCounterArray, CLOSE); + if (ret != SUCCESS) { + return ret; + } + + procMap.clear(); + return SUCCESS; +} + +void KUNPENG_PMU::EvtList::FillFields( + const size_t &start, const size_t &end, CpuTopology *cpuTopo, ProcTopology *procTopo, vector &data) +{ + for (auto i = start; i < end; ++i) { + data[i].cpuTopo = cpuTopo; + data[i].evt = this->pmuEvt->name.c_str(); + if (data[i].comm == nullptr) { + data[i].comm = procTopo->comm; + } + data[i].ts = this->ts; + } +} + +int KUNPENG_PMU::EvtList::Read(vector &data, std::vector &sampleIps) +{ + struct PmuEvtData* head = nullptr; + for (unsigned int row = 0; row < numCpu; row++) { + auto cpuTopo = this->cpuList[row].get(); + for (unsigned int col = 0; col < numPid; col++) { + auto cnt = data.size(); + int err = this->xyCounterArray[row][col]->Read(data, sampleIps); + if (err != SUCCESS) { + return err; + } + if (data.size() - cnt) { + DBG_PRINT("evt: %s pid: %d cpu: %d samples num: %d\n", pmuEvt->name.c_str(), pidList[col]->pid, + cpuTopo->coreId, data.size() - cnt); + } + // Fill event name and cpu topology. + FillFields(cnt, data.size(), cpuTopo, pidList[col].get(), data); + } + } + return SUCCESS; +} + +int KUNPENG_PMU::EvtList::Pause() +{ + return CollectorXYArrayDoTask(this->numCpu, this->numPid, this->xyCounterArray, PAUSE); +} + +std::shared_ptr KUNPENG_PMU::EvtList::MapPmuAttr(int cpu, int pid, PmuEvt* pmuEvent) +{ + switch (pmuEvent->collectType) { + case (COUNTING): + return std::make_shared(cpu, pid, pmuEvent, procMap); + case (SAMPLING): + return std::make_shared(cpu, pid, pmuEvent, procMap); + case (SPE_SAMPLING): + return std::make_shared(cpu, pid, pmuEvent, procMap); + default: + return nullptr; + }; +} \ No newline at end of file diff --git a/pmu/evt_list.h b/pmu/evt_list.h new file mode 100644 index 0000000..d34be81 --- /dev/null +++ b/pmu/evt_list.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: declaration of class EvtList with functions for managing and interacting with a list + * of performance events in the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_EVTLIST_H +#define PMU_EVTLIST_H +#include +#include +#include +#include +#include +#include "cpu_map.h" +#include "perf_counter.h" +#include "pmu.h" +#include "process_map.h" +#include "sampler.h" +#include "spe_sampler.h" + +namespace KUNPENG_PMU { +class EvtList { +public: + using ProcPtr = std::shared_ptr; + using CpuPtr = std::shared_ptr; + EvtList(std::vector &cpuList, std::vector &pidList, std::shared_ptr pmuEvt) + : cpuList(cpuList), pidList(pidList), pmuEvt(pmuEvt) + { + this->numCpu = this->cpuList.size(); + this->numPid = this->pidList.size(); + } + int Init(); + int Pause(); + int Close(); + int Start(); + int Enable(); + int Stop(); + int Read(std::vector &pmuData, std::vector &sampleIps); + + void SetTimeStamp(const int64_t ×tamp) + { + this->ts = timestamp; + } + + std::set GetFdList() const + { + return fdList; + } + + int GetEvtType() const + { + return pmuEvt->collectType; + } + +private: + using PerfEvtPtr = std::shared_ptr; + + int CollectorDoTask(PerfEvtPtr collector, int task); + int CollectorXYArrayDoTask(int numCpu, std::vector>& xyArray, int task); + void FillFields(const size_t &start, const size_t &end, CpuTopology *cpuTopo, ProcTopology *procTopo, + std::vector &pmuData); + + std::vector cpuList; + std::vector pidList; + std::shared_ptr pmuEvt; + std::vector>> xyCounterArray; + std::shared_ptr MapPmuAttr(int cpu, int pid, PmuEvt* pmuEvent); + unsigned int numCpu = 0; + unsigned int numPid = 0; + std::set fdList; + int64_t ts = 0; + std::unordered_map procMap; +}; +} // namespace KUNPENG_PMU +#endif \ No newline at end of file diff --git a/pmu/pmu_event.cpp b/pmu/pmu_event.cpp new file mode 100644 index 0000000..19f027e --- /dev/null +++ b/pmu/pmu_event.cpp @@ -0,0 +1,37 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: function for mapping system errors to custom error codes in the KUNPENG_PMU namespace + ******************************************************************************/ +#include "pcerrc.h" +#include "pmu_event.h" + +namespace KUNPENG_PMU { + int MapErrno(int sysErr) + { + switch (sysErr) { + case EPERM: + case EACCES: + return LIBPERF_ERR_NO_PERMISSION; + case EBUSY: + return LIBPERF_ERR_DEVICE_BUSY; + case EINVAL: + return LIBPERF_ERR_DEVICE_INVAL; + case ESRCH: + return LIBPERF_ERR_NO_PROC; + case EMFILE: + return LIBPERF_ERR_TOO_MANY_FD; + default: + return UNKNOWN_ERROR; + } + } +} // namespace KUNPENG_PMU \ No newline at end of file diff --git a/pmu/pmu_event.h b/pmu/pmu_event.h new file mode 100644 index 0000000..facfb82 --- /dev/null +++ b/pmu/pmu_event.h @@ -0,0 +1,166 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: definition of structures related to performance event sampling and recording in + * the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_EVENT_H +#define PMU_EVENT_H +#include +#include +#include +#include +#include +#include +#include "pmu.h" + +#ifndef PMU_SAMPLE_STRUCT_H +#define PMU_SAMPLE_STRUCT_H +#include +#include + +struct PmuEvt { + __u64 type; // pmu event type defined in linux/perf_event.h + __u64 config; // event configuration + __u64 config1; // event filter if applicable + __u64 config2; // further filter if necessary on top of config1 + int pmuType; // if pmu is CORE/UNCORE/SPE and etc (to be implemented) + int collectType; + std::string name; // string name of this pmu event + int cpumask; // a representative CPU number for each socket (package) in the motherboard. + union { + unsigned period; // sample period + unsigned freq; // sample frequency + }; + unsigned useFreq : 1; +}; + +namespace KUNPENG_PMU { + +const int PERF_SAMPLE_MAX_SIZE (1 << 16); + +struct PerfRawSample { + __u64 sampleid; + __u64 ip; + __u32 pid, tid; + __u64 time; + __u64 id; + int cpu; + __u64 period; + __u64 nr; + unsigned long ips[]; +}; + +struct PerfTraceSample { + __u64 sampleId; + __u32 tid, pid; + __u64 time; + __u32 cpu; + __u32 size; + char data[]; +}; + +struct sampleId { + __u32 pid, tid; /* if PERF_SAMPLE_TID set */ + __u64 time; /* if PERF_SAMPLE_TIME set */ + __u64 id; /* if PERF_SAMPLE_ID set */ + __u32 cpu, res; /* if PERF_SAMPLE_CPU set */ + __u64 identifier; /* if PERF_SAMPLE_IDENTIFIER set */ +}; + +struct PerfRawMmap { + __u32 pid, tid; + __u64 addr; + __u64 len; + __u64 pgoff; + char filename[]; +}; + +struct PerfRecordMmap { + struct perf_event_header header; + __u32 pid, tid; + __u64 addr; + __u64 len; + __u64 pgoff; + char filename[PATH_MAX]; + struct sampleId sampleId; +}; + +struct PerfRecordMmap2 { + struct perf_event_header header; + __u32 pid, tid; + __u64 addr; + __u64 len; + __u64 pgoff; + __u32 maj; + __u32 min; + __u64 ino; + __u64 ino_generation; + __u32 prot, flags; + char filename[]; +}; + +struct PerfRecordComm { + struct perf_event_header header; + __u32 pid, tid; + char comm[]; +}; + +struct PerfRecordSample { + struct perf_event_header header; + __u64 array[]; +}; + +struct PerfRecordFork { + struct perf_event_header header; + __u32 pid, ppid; + __u32 tid, ttid; + __u64 time; +}; + +struct PerfRecordExit { + struct perf_event_header header; + __u32 pid, ppid; + __u32 tid, ttid; + __u64 time; +}; + +struct PerfMmap { + struct perf_event_mmap_page* base; + __u64 mask; + int fd; + __u64 prev; + __u64 start; + __u64 end; + bool overwrite; + __u64 flush; + char copiedEvent[PERF_SAMPLE_MAX_SIZE]; +}; + +struct PerfSampleIps { + std::vector ips; +}; + +union PerfEvent { + struct perf_event_header header; + struct PerfRecordMmap mmap; + struct PerfRecordComm comm; + struct PerfRecordFork fork; + struct PerfRawSample sample; + struct PerfRecordExit exit; + struct PerfRecordMmap2 mmap2; +}; + +int MapErrno(int sysErr); +} // namespace KUNPENG_PMU +#endif +#endif \ No newline at end of file diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp new file mode 100644 index 0000000..b7e5c54 --- /dev/null +++ b/pmu/pmu_list.cpp @@ -0,0 +1,503 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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 +#include +#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 "pmu_list.h" +#include "common.h" + +using namespace std; +using namespace pcerr; + +namespace KUNPENG_PMU { +// Initializing pmu list singleton instance and global lock + std::mutex PmuList::pmuListMtx; + std::mutex PmuList::dataListMtx; + + int PmuList::CheckRlimit(const std::vector& cpuTopoList, const std::vector procTopoList, + const PmuTaskAttr* head) + { + unsigned long length = 0; + const PmuTaskAttr* current = head; + while (current != nullptr) { + ++length; + current = current->next; + } + unsigned long need = length * cpuTopoList.size() * procTopoList.size(); + return RaiseNumFd(need); + } + + int PmuList::Register(const int pd, PmuTaskAttr* taskParam) + { + SymResolverInit(); + SymResolverRecordKernel(); + /* Use libpfm to get the basic config for this pmu event */ + struct PmuTaskAttr* pmuTaskAttrHead = taskParam; + // Init collect type for pmu data, + // because different type has different free strategy. + auto &evtData = GetDataList(pd); + if (pmuTaskAttrHead != nullptr) { + evtData.collectType = static_cast(pmuTaskAttrHead->pmuEvt->collectType); + evtData.pd = pd; + } + /** + * Create cpu topology list + */ + std::vector cpuTopoList; + auto err = PrepareCpuTopoList(pd, pmuTaskAttrHead, cpuTopoList); + if (err != SUCCESS) { + return err; + } + + /** + * Create process topology list + */ + std::vector procTopoList; + err = PrepareProcTopoList(pmuTaskAttrHead, procTopoList); + if (err != SUCCESS) { + return err; + } + err = CheckRlimit(cpuTopoList, procTopoList, pmuTaskAttrHead); + if (err != SUCCESS) { + return err; + } + while (pmuTaskAttrHead) { + std::shared_ptr evtList = + std::make_shared(cpuTopoList, procTopoList, pmuTaskAttrHead->pmuEvt); + err = evtList->Init(); + if (err != SUCCESS) { + return err; + } + err = AddToEpollFd(pd, evtList); + if (err != SUCCESS) { + return err; + } + InsertEvtList(pd, evtList); + pmuTaskAttrHead = pmuTaskAttrHead->next; + } + return SUCCESS; + } + + int PmuList::Start(const int pd) + { + auto pmuList = GetEvtList(pd); + for (auto item : pmuList) { + item->Start(); + } + return SUCCESS; + } + + int PmuList::Pause(const int pd) + { + auto pmuList = GetEvtList(pd); + for (auto item : pmuList) { + item->Pause(); + } + return SUCCESS; + } + + std::vector& PmuList::Read(const int pd) + { + // Exchange data in to . + // Return a pointer to data. + auto& userData = ExchangeToUserData(pd); + return userData; + } + + int PmuList::ReadDataToBuffer(const int pd) + { + // Read data from prev sampling, + // and store data in . + auto &evtData = GetDataList(pd); + auto ts = GetCurrentTime(); + auto eventList = GetEvtList(pd); + for (auto item : eventList) { + item->SetTimeStamp(ts); + auto err = item->Read(evtData.data, evtData.sampleIps); + if (err != SUCCESS) { + return err; + } + } + + return SUCCESS; + } + + void PmuList::Close(const int pd) + { + auto evtList = GetEvtList(pd); + for (auto item : evtList) { + item->Close(); + } + EraseEvtList(pd); + EraseDataList(pd); + RemoveEpollFd(pd); + EraseSpeCpu(pd); + SymResolverDestroy(); + } + + int PmuList::NewPd() + { + lock_guard lg(pmuListMtx); + if (maxPd == std::numeric_limits::max()) { + // Search available pd, by search available key in pmuList. + unsigned availPd = 0; + auto findPd = pmuList.find(availPd); + while (findPd != pmuList.end()) { + ++availPd; + findPd = pmuList.find(availPd); + if (availPd == std::numeric_limits::max()) { + return -1; + } + } + maxPd = availPd; + } else { + maxPd++; + } + + return maxPd; + } + + bool PmuList::AllPmuDead(const int pd) + { + auto epollFd = GetEpollFd(pd); + if (epollFd == -1) { + return true; + } + // Check if all fds are EPOLLHUP, which represents all processes exit. + 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 lg(pmuListMtx); + return pmuList.find(pd) != pmuList.end(); + } + + void PmuList::FreeData(PmuData* pmuData) + { + EraseUserData(pmuData); + } + + int PmuList::GetTaskType(const int pd) const + { + lock_guard lg(pmuListMtx); + auto findEvtList = pmuList.find(pd); + if (findEvtList == pmuList.end()) { + return -1; + } + if (findEvtList->second.empty()) { + return -1; + } + return findEvtList->second[0]->GetEvtType(); + } + + void PmuList::InsertEvtList(const unsigned pd, std::shared_ptr evtList) + { + lock_guard lg(pmuListMtx); + pmuList[pd].push_back(evtList); + } + + std::vector>& PmuList::GetEvtList(const unsigned pd) + { + lock_guard lg(pmuListMtx); + return pmuList[pd]; + } + + void PmuList::EraseEvtList(const unsigned pd) + { + lock_guard lg(pmuListMtx); + pmuList.erase(pd); + } + + PmuList::EventData& PmuList::GetDataList(const unsigned pd) + { + lock_guard lg(dataListMtx); + return dataList[pd]; + } + + void PmuList::EraseDataList(const unsigned pd) + { + lock_guard 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) + { + // Parse dwarf and elf info of each pid and get stack trace for each pmu data. + for (size_t i = 0; i < eventData.data.size(); ++i) { + auto &pmuData = eventData.data[i]; + auto &ipsData = eventData.sampleIps[i]; + if (eventData.collectType == SPE_SAMPLING) { + SymResolverRecordModuleNoDwarf(pmuData.pid); + } else { + SymResolverRecordModule(pmuData.pid); + } + if (pmuData.stack == nullptr) { + pmuData.stack = StackToHash(pmuData.pid, ipsData.ips.data(), ipsData.ips.size()); + } + } + } + + void PmuList::AggregateData(const std::vector& evData, std::vector& newEvData) + { + // Acccumulate stat data in previous PmuCollect for convenient use. + // One count for same event + tid + cpu. + map, 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); + } + } + + std::vector& PmuList::ExchangeToUserData(const unsigned pd) + { + lock_guard lg(dataListMtx); + if (dataList.count(pd) == 0) { + return GetPreviousData(pd); + } + + auto& evData = dataList[pd]; + auto pData = evData.data.data(); + if (GetTaskType(pd) == COUNTING) { + std::vector newPmuData; + AggregateData(evData.data, newPmuData); + EventData newEvData = { + .pd = pd, + .collectType = COUNTING, + .data = newPmuData, + }; + + auto inserted = userDataList.emplace(newEvData.data.data(), move(newEvData)); + dataList.erase(pd); + return inserted.first->second.data; + } else { + auto inserted = userDataList.emplace(pData, move(evData)); + dataList.erase(pd); + FillStackInfo(inserted.first->second); + return inserted.first->second.data; + } + } + + void PmuList::EraseUserData(PmuData* pmuData) + { + lock_guard lg(dataListMtx); + auto findData = userDataList.find(pmuData); + if (findData == userDataList.end()) { + return; + } + userDataList.erase(pmuData); + } + + int PmuList::GetHistoryData(const int pd, std::vector& aggregatedData) + { + lock_guard lg(dataListMtx); + std::vector 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& PmuList::GetPreviousData(const unsigned pd) + { + std::vector* 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) + { + lock_guard lg(pmuListMtx); + // Try to create a epoll fd for current pd. + 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; + } + + // Add ring buffer fd list to epoll fd. + 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 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 lg(pmuListMtx); + auto findFd = epollList.find(pd); + if (findFd != epollList.end()) { + return findFd->second; + } + return -1; + } + + std::vector& PmuList::GetEpollEvents(const int epollFd) + { + lock_guard lg(pmuListMtx); + auto findEvts = epollEvents.find(epollFd); + if (findEvts != epollEvents.end()) { + return findEvts->second; + } + + // Cannot reach here. + throw runtime_error("cannot find epoll events."); + } + + bool PmuList::IsCpuInList(const int &cpu) const + { + lock_guard 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 lg(pmuListMtx); + speCpuList[pd].insert(cpu); + } + + void PmuList::EraseSpeCpu(const unsigned &pd) + { + lock_guard lg(pmuListMtx); + speCpuList.erase(pd); + } + + int PmuList::PrepareCpuTopoList( + const unsigned &pd, PmuTaskAttr* pmuTaskAttrHead, std::vector& cpuTopoList) + { + for (int i = 0; i < pmuTaskAttrHead->numCpu; i++) { + if (pmuTaskAttrHead->pmuEvt->collectType == SPE_SAMPLING && IsCpuInList(pmuTaskAttrHead->cpuList[i])) { + // For SPE sampling, one core can only be used by one pd. + // Therefore, check if core is in sampling. + return LIBPERF_ERR_DEVICE_BUSY; + } + struct CpuTopology* cpuTopo = GetCpuTopology(pmuTaskAttrHead->cpuList[i]); + if (cpuTopo == nullptr) { + New(LIBPERF_ERR_FAIL_GET_CPU); + return LIBPERF_ERR_FAIL_GET_CPU; + } + if (pmuTaskAttrHead->pmuEvt->collectType == SPE_SAMPLING) { + AddSpeCpu(pd, pmuTaskAttrHead->cpuList[i]); + } + cpuTopoList.emplace_back(shared_ptr(cpuTopo)); + } + return SUCCESS; + } + + int PmuList::PrepareProcTopoList(PmuTaskAttr* pmuTaskAttrHead, std::vector& procTopoList) const + { + if (pmuTaskAttrHead->numPid == 0) { + struct ProcTopology* procTopo = GetProcTopology(-1); + if (procTopo == nullptr) { + New(LIBPERF_ERR_FAIL_GET_PROC); + return LIBPERF_ERR_FAIL_GET_PROC; + } + procTopoList.emplace_back(unique_ptr(procTopo, FreeProcTopo)); + } + for (int i = 0; i < pmuTaskAttrHead->numPid; i++) { + int numChild = 0; + int* childTidList = GetChildTid(pmuTaskAttrHead->pidList[i], &numChild); + if (childTidList == nullptr) { + return LIBPERF_ERR_INVALID_PID; + } + for (int j = 0; j < numChild; j++) { + struct ProcTopology* procTopo = GetProcTopology(childTidList[j]); + if (procTopo == nullptr) { + New(LIBPERF_ERR_FAIL_GET_PROC); + return LIBPERF_ERR_FAIL_GET_PROC; + } + DBG_PRINT("Add to proc map: %d\n", childTidList[j]); + procTopoList.emplace_back(shared_ptr(procTopo, FreeProcTopo)); + } + delete[] childTidList; + } + return SUCCESS; + } + +} \ No newline at end of file diff --git a/pmu/pmu_list.h b/pmu/pmu_list.h new file mode 100644 index 0000000..a5060eb --- /dev/null +++ b/pmu/pmu_list.h @@ -0,0 +1,138 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: definition of singleton class PmuList for managing performance monitoring tasks, + * collecting data, and handling performance counters in the KUNPENG_PMU namespace + ******************************************************************************/ +#ifndef PMU_LIST_H +#define PMU_LIST_H +#include +#include +#include +#include +#include "evt_list.h" +#include "pmu_event.h" + +namespace KUNPENG_PMU { + +struct PmuTaskAttr { + int numCpu; // number of cpu to be collected + int* cpuList; // list of core ids to be collected + // list length has to be as the same as numCpu + int numPid; // number of (parent) processes to be collected + int* pidList; // list of pids(tids) to be collected + // list length has to be as the same as numPid + std::shared_ptr pmuEvt; // which pmu to be collected + struct PmuTaskAttr* next; // next task attribute +}; + +class PmuList { +public: + static PmuList* GetInstance() + { + static PmuList instance; + return &instance; + } + int Register(const int pd, PmuTaskAttr* taskParam); + /** + * @brief Read all pmu data of event list, and store data to internal buffer. + * @param pd + */ + int ReadDataToBuffer(const int pd); + /** + * @brief Read pmu data from internal buffer and return ref. + * @param pd + * @return std::vector& + */ + std::vector& Read(const int pd); + int Start(const int pd); + int Pause(const int pd); + int Close(const int pd); + int AllPmuDead(const int pd); + int IsPdAlive(const int pd) const; + int FreeData(PmuData* pmuData); + int GetTaskType(const int pd) const; + + int NewPd(); + + int GetHistoryData(const int pd, std::vector& pmuData); + +private: + using ProcPtr = std::shared_ptr; + using CpuPtr = std::shared_ptr; + PmuList() + {} + PmuList(const PmuList&) = delete; + PmuList& operator=(const PmuList&) = delete; + ~PmuList() = default; + + struct EventData { + unsigned pd; + PmuTaskType collectType; + std::vector data; + std::vector sampleIps; + }; + + void InsertEvtList(const unsigned pd, std::shared_ptr evtList); + std::vector>& GetEvtList(const unsigned pd); + void EraseEvtList(const unsigned pd); + + EventData& GetDataList(const unsigned pd); + void EraseDataList(const unsigned pd); + // Move pmu data from dataList to userDataList, + // and return ref of dataList in userDataList. + std::vector& ExchangeToUserData(const unsigned pd); + void FillStackInfo(EventData &eventData); + void EraseUserData(PmuData* pmuData); + + void AddToEpollFd(const int pd, const std::shared_ptr evtList); + void RemoveEpollFd(const int pd); + int GetEpollFd(const int pd); + std::vector& GetEpollEvents(const int epollFd); + + bool IsCpuInList(const int &cpu) const; + void AddSpeCpu(const unsigned &pd, const int &cpu); + void EraseSpeCpu(const unsigned &pd); + int PrepareCpuTopoList( + const unsigned& pd, PmuTaskAttr* pmuTaskAttrHead, std::vector& cpuTopoList); + int PrepareProcTopoList(PmuTaskAttr* pmuTaskAttrHead, std::vector& procTopoList) const; + int CheckRlimit(const std::vector& cpuTopoList, const std::vector procTopoList, + const PmuTaskAttr* head); + static void AggregateData(const std::vector& evData, std::vector& newEvData); + std::vector& GetPreviousData(const unsigned pd); + + static std::mutex pmuListMtx; + static std::mutex dataListMtx; + std::unordered_map>> pmuList; + // Key: pd + // Value: PmuData List. + // PmuData is stored here before user call . + std::unordered_map dataList; + // Key: pd + // Value: PmuData vector for raw pointer. + // PmuData is stored here before user call . + std::unordered_map userDataList; + + // Key: pd + // Value: epoll fd + std::unordered_map epollList; + // Key: epoll fd + // Value: epoll event list + std::unordered_map> epollEvents; + + // Key: pd + // Value: spe sampling cpu list. + std::unordered_map> speCpuList; + unsigned maxPd = 0; +}; +} // namespace KUNPENG_PMU +#endif \ No newline at end of file -- Gitee From 31f3e7af59cc8bc9b03118fc0f536901c080a871 Mon Sep 17 00:00:00 2001 From: Galaxy Date: Sat, 13 Apr 2024 01:48:02 -0700 Subject: [PATCH 08/14] Fix compile error --- Common.cmake | 8 +- include/pmu.h | 12 +- pmu/CMakeLists.txt | 61 ++++ pmu/decoder/arm_spe_decoder.cpp | 261 ++++++++++++++++ pmu/decoder/arm_spe_decoder.h | 48 +++ pmu/evt.h | 8 +- pmu/evt_list.cpp | 7 +- pmu/evt_list.h | 4 +- pmu/perf_counter.cpp | 10 +- pmu/pfm/core.h | 2 +- pmu/pfm/pfm.cpp | 5 +- pmu/pfm/pfm_event.h | 2 +- pmu/pmu.cpp | 528 ++++++++++++++++++++++++++++++++ pmu/pmu_event.h | 8 +- pmu/pmu_list.h | 16 +- pmu/sample_process.cpp | 5 +- pmu/sampler.cpp | 5 +- pmu/spe.cpp | 4 +- pmu/spe.h | 2 +- symbol/CMakeLists.txt | 5 +- symbol/name_resolve.cpp | 2 +- symbol/name_resolve.h | 4 +- symbol/symbol_resolve.cpp | 102 ++---- util/CMakeLists.txt | 2 +- util/cpu_map.cpp | 19 +- util/cpu_map.h | 4 +- util/linked_list.h | 8 +- util/process_map.cpp | 13 +- 28 files changed, 989 insertions(+), 166 deletions(-) create mode 100644 pmu/CMakeLists.txt create mode 100644 pmu/decoder/arm_spe_decoder.cpp create mode 100644 pmu/decoder/arm_spe_decoder.h create mode 100644 pmu/pmu.cpp diff --git a/Common.cmake b/Common.cmake index acf93e7..7a2d22a 100644 --- a/Common.cmake +++ b/Common.cmake @@ -33,15 +33,9 @@ endif() # 添加一个library #GIT_REPOSITORY ssh://git@codehub-dg-y.huawei.com:2222/hwsecurec_group/huawei_secure_c.git #GIT_TAG tag_Huawei_Secure_C_V100R001C01SPC012B002_00001 -add_library(securec STATIC IMPORTED) -set_property(TARGET securec PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/huawei_secure_c/lib/libsecurec.a) -include_directories(${THIRD_PARTY}/huawei_secure_c/include) # GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/www.sqlite.org/sqlite.git # GIT_TAG 3.40.1 -add_library(sqlite3_share STATIC IMPORTED) -set_property(TARGET sqlite3_share PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/sqlite3/lib/libsqlite3.so) -include_directories(${OPEN_SOURCE_DIR}/local/sqlite3/include) add_library(elf_static STATIC IMPORTED) set_property(TARGET elf_static PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/elfin-parser/libelf++.a) @@ -72,4 +66,4 @@ set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/go add_library(gmock_main STATIC IMPORTED) set_property(TARGET gmock_main PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock_main.a) add_library(gmock STATIC IMPORTED) -set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock.a) \ No newline at end of file +set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock.a) diff --git a/include/pmu.h b/include/pmu.h index 2e22b87..bbf9db2 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -86,7 +86,7 @@ struct PmuData { struct Stack* stack; // call stack const char *evt; // event name int64_t ts; // time stamp - pid_t pis; // process id + pid_t pid; // process id int tid; // thread id unsigned cpu; // cpu id struct CpuTopology *cpuTopo; // cpu topology @@ -116,7 +116,7 @@ int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr); /** * @brief * Collect milliseconds. If is equal to - 1 and the PID list is not empty, the collection - * is performed until all processes are compete. + * is performed until all processes are complete. * @param milliseconds * @return int */ @@ -128,13 +128,13 @@ int PmuCollect(int pd, int milliseconds); * @param milliseconds * @return int */ -int PmuCollectV(int *pd, int milliseconds); +int PmuCollectV(int *pd, unsigned len, int milliseconds); /** * @brief stop a sampling task in asynchronous mode * @param pd pmu descriptor. */ -int PmuStop(int pd); +void PmuStop(int pd); /** * @brief @@ -147,13 +147,13 @@ int PmuRead(int pd, struct PmuData** pmuData); /** * @brief Close all the file descriptor opened during collecting process */ -int PmuClose(int pd); +void PmuClose(int pd); /** * @brief Free PmuData pointer. * @param pmuData */ -void PmuDataFree(struct PmuData** pmuData); +void PmuDataFree(struct PmuData* pmuData); #pragma GCC visibility pop #ifdef __cplusplus diff --git a/pmu/CMakeLists.txt b/pmu/CMakeLists.txt new file mode 100644 index 0000000..d7a124d --- /dev/null +++ b/pmu/CMakeLists.txt @@ -0,0 +1,61 @@ +# Description: Compile Devikit rpc framework. +# Copyright: Copyright © Huawei Technologies Co., Ltd. 2023. All rights reserved. +# History: 2023-05-10 created + +if (POLICY CMP0048) + cmake_policy(SET CMP0048 NEW) +endif (POLICY CMP0048) +project(libkprof) +cmake_minimum_required (VERSION 3.12.0) +# Related directory settings # +set(UTIL_FILE_DIR ${PROJECT_TOP_DIR}/util) +set(SYMBOL_FILE_DIR ${PROJECT_TOP_DIR}/symbol) +set(SKELETON_FILE_DIR ${PROJECT_TOP_DIR}/skeleton) +set(PMU_FILE_DIR ${PROJECT_TOP_DIR}/pmu) +set(PFM_FILE_DIR ${PROJECT_TOP_DIR}/pmu/pfm) +set(PMU_DECODER_DIR ${PMU_FILE_DIR}/decoder) + +# Source files # +file(GLOB UTIL_SRC ${UTIL_FILE_DIR}/*.cpp) + +file(GLOB PMU_SRC ${PMU_FILE_DIR}/*c ${PMU_FILE_DIR}/*cpp ${PMU_FILE_DIR}/analyzer/*cpp + ${PMU_FILE_DIR}/analyzer/metric/*cpp ${PMU_FILE_DIR}/analyzer/numafast/*c + ${PMU_FILE_DIR}/analyzer/numafast/util/*c ${PMU_FILE_DIR}/analyzer/numafast/analyze/*c) +file(GLOB PMU_DECODER_SRC ${PMU_DECODER_DIR}/*.cpp) +file(GLOB SYMBOL_SRC ${SYMBOL_FILE_DIR}/*c ${SYMBOL_FILE_DIR}/*cpp) +file(GLOB PFM_SRC ${PFM_FILE_DIR}/*c ${PFM_FILE_DIR}/*cpp) + +set(PFM_FILE_DIR ${PROJECT_TOP_DIR}/pmu/pfm) +file(GLOB PFM_SRC ${PFM_FILE_DIR}/*c ${PFM_FILE_DIR}/*cpp) + +include_directories(${PROJECT_TOP_DIR}/include) +include_directories(${PMU_FILE_DIR}/) +include_directories(${PROJECT_TOP_DIR}/include) +include_directories(${PFM_FILE_DIR}) +include_directories(${PROJECT_TOP_DIR}/include) + +# directories for ultilities and symbol resolving +include_directories(${UTIL_FILE_DIR}) +include_directories(${SYMBOL_FILE_DIR}) +include_directories(${PMU_FILE_DIR}/analyzer) +include_directories(${PMU_FILE_DIR}/analyzer/metric) +include_directories(${PMU_FILE_DIR}/analyzer/numafast) +include_directories(${PMU_FILE_DIR}/analyzer/numafast/util) +include_directories(${PMU_FILE_DIR}/analyzer/numafast/analyze) +include_directories(${PMU_DECODER_DIR}) +# include skeleton files +include_directories(${SKELETON_FILE_DIR}) + +# include all the procfs related header files +include_directories(${PROCFS_FILE_DIR}) +include_directories(${PROCFS_FILE_DIR}/analyze) +include_directories(${PROCFS_FILE_DIR}/parser) + +#include thirdparty +include_directories(${THIRD_PARTY}/json/include) + +set(CMAKE_SKIP_RPATH YES) + +ADD_LIBRARY(perf SHARED ${PMU_SRC} ${UTIL_SRC} ${PFM_SRC} ${PMU_DECODER_SRC}) +target_link_libraries(perf numa sym) +target_compile_options(perf PRIVATE -fPIC) diff --git a/pmu/decoder/arm_spe_decoder.cpp b/pmu/decoder/arm_spe_decoder.cpp new file mode 100644 index 0000000..466f016 --- /dev/null +++ b/pmu/decoder/arm_spe_decoder.cpp @@ -0,0 +1,261 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Jin + * Create: 2024-04-03 + * Description: parses the SPE data in the AUX buffer + ******************************************************************************/ +#include +#include +#include +#include "spe.h" +#include "arm_spe_decoder.h" + +static void SetPktPayload(struct SpePacket *pkt, uint8_t *buf) +{ + switch (pkt->payloadSize) { + case 1: + pkt->payload = *buf; + break; + case 2: + pkt->payload = *(uint16_t *)(buf); + break; + case 4: + pkt->payload = *(uint32_t *)(buf); + break; + case 8: + pkt->payload = *(uint64_t *)(buf); + break; + default: + break; + } +} + +static inline void* SpePacketPad(struct SpePacket *pkt, uint8_t *buf) +{ + pkt->type = SpePacketType::SPE_PACKET_PAD; + buf += sizeof(uint8_t); + return buf; +} + +static inline void* SpePacketEnd(struct SpePacket *pkt, uint8_t *buf) +{ + pkt->type = SpePacketType::SPE_PACKET_END; + buf += sizeof(uint8_t); + return buf; +} + +static inline void* SpePacketTs(uint16_t header, struct SpePacket *pkt, uint8_t *buf) +{ + pkt->type = SpePacketType::SPE_PACKET_TIMESTAMP; + buf += sizeof(uint8_t); + pkt->payloadSize = 1 << ((header & 0b110000) >> 4); + SetPktPayload(pkt, buf); + buf += pkt->payloadSize; + return buf; +} + +static uint8_t* Get0B01Pkt(uint16_t header, struct SpePacket *pkt, uint8_t *buf) +{ + if ((header & 0b1111) == 0b0010) { + pkt->type = SpePacketType::SPE_PACKET_EVENTS; + buf += sizeof(uint8_t); + pkt->payloadSize = 1 << ((header & 0b110000) >> 4); + SetPktPayload(pkt, buf); + buf += pkt->payloadSize; + } else if ((header & 0b1111) == 0b0011) { + pkt->type = SpePacketType::SPE_PACKET_DATA_SOURCE; + buf += sizeof(uint8_t); + pkt->payloadSize = 1 << ((header & 0b110000) >> 4); + buf += pkt->payloadSize; + } else if ((header >> 2) == 0b011001) { + pkt->type = SpePacketType::SPE_PACKET_CONTEXT; + buf += sizeof(uint8_t); + pkt->payloadSize = 1 << ((header & 0b110000) >> 4); + SetPktPayload(pkt, buf); + buf += pkt->payloadSize; + } else if ((header >> 2) == 0b010010) { + pkt->type = SpePacketType::SPE_PACKET_OP_TYPE; + buf += sizeof(uint8_t); + buf += sizeof(uint8_t); + } else { + pkt->type = SpePacketType::SPE_PACKET_BAD; + buf += sizeof(uint8_t); + } + + return buf; +} + +static uint8_t *GetPkt(struct SpePacket *pkt, uint8_t *buf) +{ + uint16_t header = *static_cast(buf); + + switch (header) { + case (0): + return static_cast(SpePacketPad(pkt, buf)); + case (1): + return static_cast(SpePacketEnd(pkt, buf)); + case (0b01110001): + return static_cast(SpePacketTs(header, pkt, buf)); + default: + break; + } + + if (!((header >> 6) ^ 0b01)) { + buf = Get0B01Pkt(header, pkt, buf); + } else if ((header >> 3) == 0b10110) { + pkt->type = SpePacketType::SPE_PACKET_ADDRESS; + buf += sizeof(uint8_t); + pkt->payloadSize = 1 << ((header & 0b110000) >> 4); + SetPktPayload(pkt, buf); + buf += pkt->payloadSize; + } else if ((header >> 3) == 0b10011) { + pkt->type = SpePacketType::SPE_PACKET_COUNTER; + buf += sizeof(uint8_t); + buf += sizeof(uint16_t); + } else if ((header >> 10) == 0b001000) { + header = *(uint16_t *)(buf); + if ((header & 0b11111000) == 0b10110000) { + pkt->type = SpePacketType::SPE_PACKET_ADDRESS; + } else if ((header & 0b11111000) == 0b10011000) { + pkt->type = SpePacketType::SPE_PACKET_COUNTER; + } else { + pkt->type = SpePacketType::SPE_PACKET_BAD; + } + buf += sizeof(uint16_t); + pkt->payloadSize = 1 << ((header & 0b110000) >> 4); + SetPktPayload(pkt, buf); + buf += pkt->payloadSize; + } else { + pkt->type = SpePacketType::SPE_PACKET_BAD; + buf += sizeof(uint8_t); + } + + pkt->header = header; + return buf; +} + +static uint64_t FixupTopByte(uint64_t va) +{ + uint64_t fixup = (va & 0xff000000000000) >> 48; + + /* + * Armv8 ARM (ARM DDI 0487F.c), chapter "D10.2.1 Address packet" + * defines the data virtual address payload format, the top byte + * (bits [63:56]) is assigned as top-byte tag; so we only can + * retrieve address value from bits [55:0]. + * + * According to Documentation/arm64/memory.rst, if detects the + * specific pattern in bits [55:52] of payload which falls in + * the kernel space, should fixup the top byte. + * + * For this reason, if detects the bits [55:52] is 0xf, will + * fill 0xff into the top byte. + */ + if ((fixup & 0xf0ULL) == 0xf0ULL) { + va |= 0xffULL << 56; + } + + return va; +} + +static void DecodeAddressPkt(struct SpePacket *pkt, struct SpeRecord *record) +{ + uint16_t index = (pkt->header & 0b111) | (((pkt->header >> 8) & 0b11) << 3); + + switch (index) { + case 0: // PC + record->pc = pkt->payload & 0xffffffffffffff; + record->pc = FixupTopByte(record->pc); + break; + case 1: // Branch target address + break; + case 2: // Data access virtual address + record->va = pkt->payload & 0xffffffffffffff; + record->va = FixupTopByte(record->va); + break; + case 3: // Data access physical address + record->pa = pkt->payload & 0xffffffffffffff; + break; + case 4: // Previous branch target address + break; + default: + break; + } +} + +static void DecodeEventPkt(struct SpePacket *pkt, struct SpeRecord *record) +{ + record->event = pkt->payload; +} + +static void DecodeContextPkt(struct SpePacket *pkt, struct SpeRecord *record) +{ + record->tid = pkt->payload; +} + +static void DecodeTimestampPkt(struct SpePacket *pkt, struct SpeRecord *record) +{ + record->timestamp = pkt->payload; +} + +static void DecodePkt(struct SpePacket *pkt, struct SpeRecord *record) +{ + switch (pkt->type) { + case SpePacketType::SPE_PACKET_ADDRESS: + DecodeAddressPkt(pkt, record); + break; + case SpePacketType::SPE_PACKET_CONTEXT: + DecodeContextPkt(pkt, record); + break; + case SpePacketType::SPE_PACKET_COUNTER: + break; + case SpePacketType::SPE_PACKET_DATA_SOURCE: + break; + case SpePacketType::SPE_PACKET_END: + break; + case SpePacketType::SPE_PACKET_EVENTS: + DecodeEventPkt(pkt, record); + break; + case SpePacketType::SPE_PACKET_OP_TYPE: + break; + case SpePacketType::SPE_PACKET_PAD: + break; + case SpePacketType::SPE_PACKET_TIMESTAMP: + DecodeTimestampPkt(pkt, record); + break; + default: + break; + } +} + +SpeRecord *SpeGetRecord(uint8_t *buf, uint8_t *end, struct SpeRecord *rec, int *remainSize) +{ + struct SpePacket pkt; + + rec->pid = -1; + rec->tid = -1; + while (buf < end) { + if (*remainSize < 1) { + break; + } + + buf = GetPkt(&pkt, buf); + DecodePkt(&pkt, rec); + if (pkt.type == SpePacketType::SPE_PACKET_END || pkt.type == SpePacketType::SPE_PACKET_TIMESTAMP) { + rec++; + *remainSize -= 1; + rec->pid = -1; + rec->tid = -1; + } + } + + return rec; +} diff --git a/pmu/decoder/arm_spe_decoder.h b/pmu/decoder/arm_spe_decoder.h new file mode 100644 index 0000000..65d247e --- /dev/null +++ b/pmu/decoder/arm_spe_decoder.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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.Jin + * Create: 2024-04-03 + * Description: get-spe-data interface and spe-packet definitions + ******************************************************************************/ +#ifndef __SPE__DECODER_HH__ +#define __SPE__DECODER_HH__ + +#include +#include +#include +#include +#include + +enum class SpePacketType { + SPE_PACKET_BAD, + SPE_PACKET_ADDRESS, + SPE_PACKET_CONTEXT, + SPE_PACKET_COUNTER, + SPE_PACKET_DATA_SOURCE, + SPE_PACKET_END, + SPE_PACKET_EVENTS, + SPE_PACKET_OP_TYPE, + SPE_PACKET_PAD, + SPE_PACKET_TIMESTAMP, +}; + +struct SpePacket { + enum SpePacketType type; + uint64_t payload; + uint16_t header; + uint16_t payloadSize; +}; + +struct SpeRecord; + +SpeRecord *SpeGetRecord(uint8_t *buf, uint8_t *end, struct SpeRecord *rec, int *remainSize); + +#endif diff --git a/pmu/evt.h b/pmu/evt.h index bb6fc49..1275229 100644 --- a/pmu/evt.h +++ b/pmu/evt.h @@ -27,7 +27,7 @@ namespace KUNPENG_PMU { class PerfEvt { public: using ProcPtr = std::shared_ptr; - using ProcMap = std::shared_ptr; + using ProcMap = std::unordered_map; PerfEvt(int cpu, int pid, struct PmuEvt* evt, ProcMap& procMap) : cpu(cpu), pid(pid), evt(evt), procMap(procMap) {} @@ -40,7 +40,7 @@ public: virtual bool Reset(); virtual bool Close(); - virtual int Init(); + virtual int Init() = 0; virtual int Read(std::vector &data, std::vector &sampleIps) = 0; @@ -52,7 +52,7 @@ public: } protected: - __u64 const; + __u64 count; int fd; int cpu; pid_t pid; @@ -62,4 +62,4 @@ protected: int PerfEventOpen(struct perf_event_attr* attr, pid_t pid, int cpu, int groupFd, unsigned long flags); __u64 ReadOnce(__u64 *head); } // namespace KUNPENG_PMU -#endif \ No newline at end of file +#endif diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp index 2ad5489..3ef75f1 100644 --- a/pmu/evt_list.cpp +++ b/pmu/evt_list.cpp @@ -17,7 +17,6 @@ #include #include #include -#include "securec.h" #include "cpu_map.h" #include "linked_list.h" #include "pmu_event.h" @@ -66,9 +65,7 @@ int KUNPENG_PMU::EvtList::CollectorDoTask(PerfEvtPtr collector, int task) return UNKNOWN_ERROR; } } - -int KUNPENG_PMU::EvtList::CollectorXYArrayDoTask( - int cpuCnt, int pidCnt, std::vector>& xyArray, int task) +int KUNPENG_PMU::EvtList::CollectorXYArrayDoTask(int cpuCnt, int pidCnt, std::vector>& xyArray, int task) { for (int row = 0; row < cpuCnt; row++) { for (int col = 0; col < pidCnt; col++) { @@ -187,4 +184,4 @@ std::shared_ptr KUNPENG_PMU::EvtList::MapPmuAttr(int cpu, default: return nullptr; }; -} \ No newline at end of file +} diff --git a/pmu/evt_list.h b/pmu/evt_list.h index d34be81..e471764 100644 --- a/pmu/evt_list.h +++ b/pmu/evt_list.h @@ -65,7 +65,7 @@ private: using PerfEvtPtr = std::shared_ptr; int CollectorDoTask(PerfEvtPtr collector, int task); - int CollectorXYArrayDoTask(int numCpu, std::vector>& xyArray, int task); + int CollectorXYArrayDoTask(int numCpu, int numPid, std::vector>& xyArray, int task); void FillFields(const size_t &start, const size_t &end, CpuTopology *cpuTopo, ProcTopology *procTopo, std::vector &pmuData); @@ -81,4 +81,4 @@ private: std::unordered_map procMap; }; } // namespace KUNPENG_PMU -#endif \ No newline at end of file +#endif diff --git a/pmu/perf_counter.cpp b/pmu/perf_counter.cpp index cb04a5e..6262003 100644 --- a/pmu/perf_counter.cpp +++ b/pmu/perf_counter.cpp @@ -21,7 +21,6 @@ #include #include #include -#include "securec.h" #include "pmu.h" #include "linked_list.h" #include "pmu_event.h" @@ -60,7 +59,8 @@ int KUNPENG_PMU::PerfCounter::Read(vector &data, std::vectorcount = perfCountValue.value; + this->count = perfCountValue.value * static_cast(perfCountValue.timeEnabled) / + static_cast(perfCountValue.timeRunning); data.emplace_back(PmuData{0}); auto& current = data.back(); current.count = this->count; @@ -88,9 +88,7 @@ int KUNPENG_PMU::PerfCounter::MapPerfAttr() * added soon */ struct perf_event_attr attr; - if (memset_s(&attr, MAX_ATTR_SIZE, 0, sizeof(attr)) != EOK) { - return UNKNOWN_ERROR; - } + memset(&attr, 0, sizeof(attr)); attr.size = sizeof(struct perf_event_attr); attr.type = this->evt->type; attr.config = this->evt->config; @@ -111,4 +109,4 @@ int KUNPENG_PMU::PerfCounter::MapPerfAttr() return MapErrno(errno); } return SUCCESS; -} \ No newline at end of file +} diff --git a/pmu/pfm/core.h b/pmu/pfm/core.h index 0afe1c7..1e0ca22 100644 --- a/pmu/pfm/core.h +++ b/pmu/pfm/core.h @@ -19,7 +19,7 @@ #include "pfm_name.h" namespace KUNPENG_PMU { - extern const KUNPENG_PMU::CORE_EVT_MAP CORE_EVENT_MAP; + extern const CORE_EVT_MAP CORE_EVENT_MAP; } #endif diff --git a/pmu/pfm/pfm.cpp b/pmu/pfm/pfm.cpp index 99e9b98..2757120 100644 --- a/pmu/pfm/pfm.cpp +++ b/pmu/pfm/pfm.cpp @@ -22,7 +22,6 @@ #include #include #include "trace.h" -#include "securec.h" #include "common.h" #include "cpu_map.h" #include "pfm_event.h" @@ -99,7 +98,7 @@ static int GetSpeType(void) return -1; } - if (fscanf_s(fp, "%d", &type, sizeof(int)) != 1) { + if (fscanf(fp, "%d", &type) != 1) { if (fclose(fp) == EOF) { return -1; } @@ -205,4 +204,4 @@ void PmuEvtFree(PmuEvt *evt) if (evt != nullptr) { delete evt; } -} \ No newline at end of file +} diff --git a/pmu/pfm/pfm_event.h b/pmu/pfm/pfm_event.h index 3879c43..b416baf 100644 --- a/pmu/pfm/pfm_event.h +++ b/pmu/pfm/pfm_event.h @@ -49,4 +49,4 @@ namespace KUNPENG_PMU { using CORE_EVT_MAP = std::unordered_map&>; } // namespace KUNPENG_PMU -#endif \ No newline at end of file +#endif diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp new file mode 100644 index 0000000..9859a20 --- /dev/null +++ b/pmu/pmu.cpp @@ -0,0 +1,528 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: implementations for managing performance monitoring tasks, collecting data, + * and handling performance counters in the KUNPENG_PMU namespace + ******************************************************************************/ +#include +#include +#include +#include "pfm.h" +#include "pfm_event.h" +#include "pmu_event.h" +#include "pmu_list.h" +#include "linked_list.h" +#include "pcerr.h" +#include "safe_handler.h" +#include "pmu.h" + +using namespace pcerr; +using namespace KUNPENG_PMU; +using namespace std; + +#define MAX_CPU_NUM sysconf(_SC_NPROCESSORS_ONLN) + +static unordered_map runningStatus; +static SafeHandler pdMutex; + +struct PmuTaskAttr* AssignPmuTaskParam(PmuTaskType collectType, struct PmuAttr *attr); + +static int PmuCollectStart(const int pd) +{ + return KUNPENG_PMU::PmuList::GetInstance()->Start(pd); +} + +static int PmuCollectPause(const int pd) +{ + return KUNPENG_PMU::PmuList::GetInstance()->Pause(pd); +} + +static int CheckCpuList(unsigned numCpu, int* cpuList) +{ + if (numCpu > MAX_CPU_NUM) { + string errMsg = "Invalid numCpu: " + to_string(numCpu); + New(LIBPERF_ERR_INVALID_CPULIST, errMsg); + return LIBPERF_ERR_INVALID_CPULIST; + } + if (numCpu > 0 && cpuList == nullptr) { + New(LIBPERF_ERR_INVALID_CPULIST); + return LIBPERF_ERR_INVALID_CPULIST; + } + for (int i = 0; i < numCpu; i++) { + if (cpuList[i] < 0 || cpuList[i] >= MAX_CPU_NUM) { + string errMsg = "Invalid cpu id: " + to_string(cpuList[i]); + New(LIBPERF_ERR_INVALID_CPULIST, errMsg); + return LIBPERF_ERR_INVALID_CPULIST; + } + } + return SUCCESS; +} + +static int CheckPidList(unsigned numPid, int* pidList) +{ + if (numPid > 0 && pidList == nullptr) { + New(LIBPERF_ERR_INVALID_PIDLIST); + return LIBPERF_ERR_INVALID_PIDLIST; + } + for (int i = 0; i < numPid; i++) { + if (pidList[i] < 0) { + string errMsg = "Invalid pid: " + to_string(pidList[i]); + New(LIBPERF_ERR_INVALID_PIDLIST, errMsg); + return LIBPERF_ERR_INVALID_PIDLIST; + } + } + return SUCCESS; +} + +static int CheckEvtList(unsigned numEvt, char** evtList) +{ + if (numEvt > 0 && evtList == nullptr) { + New(LIBPERF_ERR_INVALID_EVTLIST); + return LIBPERF_ERR_INVALID_EVTLIST; + } + return SUCCESS; +} + +static int CheckAttr(enum PmuTaskType collectType, struct PmuAttr *attr) +{ + auto err = CheckCpuList(attr->numCpu, attr->cpuList); + if (err != SUCCESS) { + return err; + } + err = CheckPidList(attr->numPid, attr->pidList); + if (err != SUCCESS) { + return err; + } + err = CheckEvtList(attr->numEvt, attr->evtList); + if (err != SUCCESS) { + return err; + } + if (collectType < 0 || collectType >= MAX_TASK_TYPE) { + New(LIBPERF_ERR_INVALID_TASK_TYPE); + return LIBPERF_ERR_INVALID_TASK_TYPE; + } + if ((collectType == SAMPLING || collectType == COUNTING) && attr->evtList == nullptr) { + New(LIBPERF_ERR_INVALID_EVTLIST); + return LIBPERF_ERR_INVALID_EVTLIST; + } + + return SUCCESS; +} + +static bool PdValid(const int &pd) +{ + return PmuList::GetInstance()->IsPdAlive(pd); +} + +static void PmuTaskAttrFree(PmuTaskAttr *taskAttr) +{ + auto node = taskAttr; + while (node) { + delete[] node->pidList; + delete[] node->cpuList; + auto current = node; + node = node->next; + current->pmuEvt = nullptr; + free(current); + } +} + +int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr) +{ + try { + auto err = CheckAttr(collectType, attr); + if (err != SUCCESS) { + return -1; + } + + auto pTaskAttr = AssignPmuTaskParam(collectType, attr); + if (pTaskAttr == nullptr) { + return -1; + } + unique_ptr taskAttr(pTaskAttr, PmuTaskAttrFree); + + auto pd = KUNPENG_PMU::PmuList::GetInstance()->NewPd(); + if (pd == -1) { + New(LIBPERF_ERR_NO_AVAIL_PD); + return -1; + } + + err = KUNPENG_PMU::PmuList::GetInstance()->Register(pd, taskAttr.get()); + if (err != SUCCESS) { + PmuList::GetInstance()->Close(pd); + pd = -1; + } + New(err); + return pd; + } catch (std::bad_alloc&) { + New(COMMON_ERR_NOMEM); + return -1; + } catch (exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + return -1; + } +} + +static int DoCollectCounting(int pd, int milliseconds) +{ + constexpr int collectInterval = 100; + constexpr int usecPerMilli = 1000; + // Collect every milliseconds, + // and read data from ring buffer. + int remained = milliseconds; + bool unlimited = milliseconds == -1; + PmuCollectStart(pd); + while (remained > 0 || unlimited) { + int interval = collectInterval; + if (!unlimited && remained < collectInterval) { + interval = remained; + } + usleep(usecPerMilli * interval); + + pdMutex.tryLock(pd); + if (!runningStatus[pd]) { + pdMutex.releaseLock(pd); + break; + } + pdMutex.releaseLock(pd); + + remained -= interval; + } + PmuCollectPause(pd); + // Read data from ring buffer and store data to somewhere. + auto err = PmuList::GetInstance()->ReadDataToBuffer(pd); + if (err != SUCCESS) { + New(err); + return err; + } + return SUCCESS; +} + +static int DoCollectNonCounting(int pd, int milliseconds) +{ + constexpr int collectInterval = 100; + constexpr int usecPerMilli = 1000; + // Collect every milliseconds, + // and read data from ring buffer. + int remained = milliseconds; + bool unlimited = milliseconds == -1; + while (remained > 0 || unlimited) { + int interval = collectInterval; + if (!unlimited && remained < collectInterval) { + interval = remained; + } + + PmuCollectStart(pd); + usleep(usecPerMilli * interval); + PmuCollectPause(pd); + + // Read data from ring buffer and store data to somewhere. + auto err = PmuList::GetInstance()->ReadDataToBuffer(pd); + if (err != SUCCESS) { + New(err); + return err; + } + + // Check if all processes exit. + if (PmuList::GetInstance()->AllPmuDead(pd)) { + break; + } + pdMutex.tryLock(pd); + if (!runningStatus[pd]) { + pdMutex.releaseLock(pd); + break; + } + pdMutex.releaseLock(pd); + + remained -= interval; + } + return SUCCESS; +} + +static int DoCollect(int pd, int milliseconds) +{ + if (PmuList::GetInstance()->GetTaskType(pd) == COUNTING) { + return DoCollectCounting(pd, milliseconds); + } + return DoCollectNonCounting(pd, milliseconds); +} + +int PmuCollect(int pd, int milliseconds) +{ + int err = SUCCESS; + string errMsg = ""; + try { + if (!PdValid(pd)) { + New(LIBPERF_ERR_INVALID_PD); + return -1; + } + if (milliseconds != -1 && milliseconds < 0) { + New(LIBPERF_ERR_INVALID_TIME); + return -1; + } + + pdMutex.tryLock(pd); + runningStatus[pd] = true; + pdMutex.releaseLock(pd); + err = DoCollect(pd, milliseconds); + } catch (std::bad_alloc&) { + err = COMMON_ERR_NOMEM; + } catch (exception& ex) { + err = UNKNOWN_ERROR; + errMsg = ex.what(); + } + pdMutex.tryLock(pd); + runningStatus[pd] = false; + pdMutex.releaseLock(pd); + if (!errMsg.empty()) { + New(err, errMsg); + } else { + New(err); + } + if (err != SUCCESS) { + return -1; + } + return err; +} + +static int InnerCollect(int *pds, unsigned len, size_t collectTime, bool &stop) +{ + for (unsigned i = 0; i < len; ++i) { + PmuCollectStart(pds[i]); + } + usleep(collectTime); + for (unsigned i = 0; i < len; ++i) { + PmuCollectPause(pds[i]); + } + + for (unsigned i = 0; i < len; ++i) { + // Read data from ring buffer and store data to somewhere. + auto err = PmuList::GetInstance()->ReadDataToBuffer(pds[i]); + if (err != SUCCESS) { + return err; + } + } + + // Check if all processes exit. + bool allDead = true; + for (unsigned i = 0; i < len; ++i) { + auto taskType = PmuList::GetInstance()->GetTaskType(pds[i]); + if (taskType == COUNTING) { + allDead = false; + break; + } + if (!PmuList::GetInstance()->AllPmuDead(pds[i])) { + allDead = false; + break; + } + } + if (allDead) { + stop = true; + return SUCCESS; + } + + // Check if all processes are stopped. + bool allStopped = true; + for (unsigned i = 0; i < len; ++i) { + pdMutex.tryLock(pds[i]); + if (runningStatus[pds[i]]) { + allStopped = false; + pdMutex.releaseLock(pds[i]); + break; + } + pdMutex.releaseLock(pds[i]); + } + if (allStopped) { + stop = true; + } + + return SUCCESS; +} + +int PmuCollectV(int *pds, unsigned len, int milliseconds) +{ + constexpr int collectInterval = 100; + constexpr int usecPerMilli = 1000; + // Collect every milliseconds, + // and read data from ring buffer. + int remained = milliseconds; + bool unlimited = milliseconds == -1; + for (int i = 0; i < len; ++i) { + pdMutex.tryLock(pds[i]); + runningStatus[pds[i]] = true; + pdMutex.releaseLock(pds[i]); + } + while (remained > 0 || unlimited) { + int interval = collectInterval; + if (!unlimited && remained < collectInterval) { + interval = remained; + } + bool stop = false; + auto err = InnerCollect(pds, len, static_cast(usecPerMilli * interval), stop); + if (err != SUCCESS) { + New(err); + return err; + } + if (stop) { + break; + } + remained -= interval; + } + return SUCCESS; +} + +void PmuStop(int pd) +{ + if (!PdValid(pd)) { + New(LIBPERF_ERR_INVALID_PD); + return; + } + + pdMutex.tryLock(pd); + runningStatus[pd] = false; + pdMutex.releaseLock(pd); + New(SUCCESS); +} + +int PmuRead(int pd, struct PmuData** pmuData) +{ + try { + if (!PdValid(pd)) { + New(LIBPERF_ERR_INVALID_PD); + return LIBPERF_ERR_INVALID_PD; + } + + auto& retData = KUNPENG_PMU::PmuList::GetInstance()->Read(pd); + New(SUCCESS); + if (!retData.empty()) { + *pmuData = retData.data(); + return retData.size(); + } else { + *pmuData = nullptr; + return 0; + } + } catch (std::bad_alloc&) { + New(COMMON_ERR_NOMEM); + return -1; + } catch (exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + return -1; + } +} + +void PmuClose(int pd) +{ + if (!PdValid(pd)) { + New(LIBPERF_ERR_INVALID_PD); + return; + } + try { + KUNPENG_PMU::PmuList::GetInstance()->Close(pd); + New(SUCCESS); + } catch (std::bad_alloc&) { + New(COMMON_ERR_NOMEM); + } catch (exception& ex) { + New(UNKNOWN_ERROR, ex.what()); + } +} + +static struct PmuEvt* GetPmuEvent(const char* pmuName, int collectType) +{ + return PfmGetPmuEvent(pmuName, collectType); +} + +static void PrepareCpuList(PmuAttr *attr, PmuTaskAttr *taskParam, PmuEvt* pmuEvt) +{ + if (pmuEvt->cpumask >= 0) { + taskParam->numCpu = 1; + taskParam->cpuList = new int[1]; + taskParam->cpuList[0] = pmuEvt->cpumask; + } else if (attr->cpuList == nullptr && attr->pidList != nullptr && pmuEvt->collectType == COUNTING) { + // For counting with pid list for system wide, open fd with cpu -1 and specific pid. + taskParam->numCpu = 1; + taskParam->cpuList = new int[taskParam->numCpu]; + taskParam->cpuList[0] = -1; + } else if (attr->cpuList == nullptr) { + // For null cpulist, open fd with cpu 0,1,2...max_cpu + taskParam->numCpu = MAX_CPU_NUM; + taskParam->cpuList = new int[taskParam->numCpu]; + for (int i = 0; i < taskParam->numCpu; i++) { + taskParam->cpuList[i] = i; + } + } else { + taskParam->numCpu = attr->numCpu; + taskParam->cpuList = new int[attr->numCpu]; + for (int i = 0; i < attr->numCpu; i++) { + taskParam->cpuList[i] = attr->cpuList[i]; + } + } +} + +static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *attr, const char* name) +{ + unique_ptr taskParam(CreateNode(), PmuTaskAttrFree); + /** + * Assign pids to collect + */ + taskParam->numPid = attr->numPid; + taskParam->pidList = new int[attr->numPid]; + for (int i = 0; i < attr->numPid; i++) { + taskParam->pidList[i] = attr->pidList[i]; + } + PmuEvt* pmuEvt = nullptr; + if (collectType == SPE_SAMPLING) { + pmuEvt = PfmGetSpeEvent(attr->dataFilter, attr->evFilter, attr->minLatency, collectType); + if (pmuEvt == nullptr) { + New(LIBPERF_ERR_SPE_UNAVAIL); + return nullptr; + } + } else { + pmuEvt = GetPmuEvent(name, collectType); + if (pmuEvt == nullptr) { + New(LIBPERF_ERR_INVALID_EVENT, "Invalid event: " + string(name)); + return nullptr; + } + } + /** + * Assign cpus to collect + */ + PrepareCpuList(attr, taskParam.get(), pmuEvt); + + taskParam->pmuEvt = shared_ptr(pmuEvt, PmuEvtFree); + taskParam->pmuEvt->useFreq = attr->useFreq; + taskParam->pmuEvt->period = attr->period; + return taskParam.release(); +} + +struct PmuTaskAttr* AssignPmuTaskParam(enum PmuTaskType collectType, struct PmuAttr *attr) +{ + struct PmuTaskAttr* taskParam = nullptr; + if (collectType == SPE_SAMPLING) { + // evtList is nullptr, cannot loop over evtList. + taskParam = AssignTaskParam(collectType, attr, nullptr); + return taskParam; + } + for (int i = 0; i < attr->numEvt; i++) { + struct PmuTaskAttr* current = AssignTaskParam(collectType, attr, attr->evtList[i]); + if (current == nullptr) { + return nullptr; + } + AddTail(&taskParam, ¤t); + } + return taskParam; +} + +void PmuDataFree(struct PmuData* pmuData) +{ + PmuList::GetInstance()->FreeData(pmuData); + New(SUCCESS); +} diff --git a/pmu/pmu_event.h b/pmu/pmu_event.h index facfb82..b5e3bd9 100644 --- a/pmu/pmu_event.h +++ b/pmu/pmu_event.h @@ -123,14 +123,14 @@ struct PerfRecordSample { struct PerfRecordFork { struct perf_event_header header; __u32 pid, ppid; - __u32 tid, ttid; + __u32 tid, ptid; __u64 time; }; struct PerfRecordExit { struct perf_event_header header; __u32 pid, ppid; - __u32 tid, ttid; + __u32 tid, ptid; __u64 time; }; @@ -155,7 +155,7 @@ union PerfEvent { struct PerfRecordMmap mmap; struct PerfRecordComm comm; struct PerfRecordFork fork; - struct PerfRawSample sample; + struct PerfRecordSample sample; struct PerfRecordExit exit; struct PerfRecordMmap2 mmap2; }; @@ -163,4 +163,4 @@ union PerfEvent { int MapErrno(int sysErr); } // namespace KUNPENG_PMU #endif -#endif \ No newline at end of file +#endif diff --git a/pmu/pmu_list.h b/pmu/pmu_list.h index a5060eb..d249769 100644 --- a/pmu/pmu_list.h +++ b/pmu/pmu_list.h @@ -56,10 +56,10 @@ public: std::vector& Read(const int pd); int Start(const int pd); int Pause(const int pd); - int Close(const int pd); - int AllPmuDead(const int pd); - int IsPdAlive(const int pd) const; - int FreeData(PmuData* pmuData); + void Close(const int pd); + bool AllPmuDead(const int pd); + bool IsPdAlive(const int pd) const; + void FreeData(PmuData* pmuData); int GetTaskType(const int pd) const; int NewPd(); @@ -94,7 +94,7 @@ private: void FillStackInfo(EventData &eventData); void EraseUserData(PmuData* pmuData); - void AddToEpollFd(const int pd, const std::shared_ptr evtList); + int AddToEpollFd(const int pd, const std::shared_ptr &evtList); void RemoveEpollFd(const int pd); int GetEpollFd(const int pd); std::vector& GetEpollEvents(const int epollFd); @@ -117,9 +117,9 @@ private: // Value: PmuData List. // PmuData is stored here before user call . std::unordered_map dataList; - // Key: pd + // Key: PmuData raw pointer // Value: PmuData vector for raw pointer. - // PmuData is stored here before user call . + // PmuData is stored here after user call . std::unordered_map userDataList; // Key: pd @@ -135,4 +135,4 @@ private: unsigned maxPd = 0; }; } // namespace KUNPENG_PMU -#endif \ No newline at end of file +#endif diff --git a/pmu/sample_process.cpp b/pmu/sample_process.cpp index e54c5a7..79a20e7 100644 --- a/pmu/sample_process.cpp +++ b/pmu/sample_process.cpp @@ -15,7 +15,6 @@ #include #include #include -#include "securec.h" #include "pcerrc.h" #include "evt.h" #include "sample_process.h" @@ -81,9 +80,7 @@ void CopyDataInWhileLoop(KUNPENG_PMU::PerfMmap& map, __u64 offset, unsigned char __u64 restSize = offset & map.mask; copiedData = map.mask + 1 - restSize < len ? map.mask + 1 - restSize : len; - if (memcpy_s(tmpDataPtr, MAX_DATA_SIZE, &data[restSize], copiedData) != EOK) { - perror("failed to memcpy_s"); - } + memcpy(tmpDataPtr, &data[restSize], copiedData); offset += copiedData; tmpDataPtr += copiedData; diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index b323a3e..b85fed7 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -23,7 +23,6 @@ #include #include #include -#include "securec.h" #include "linked_list.h" #include "symbol_resolve.h" #include "util_time.h" @@ -49,9 +48,7 @@ static inline bool IsPowerOfTwo(T x) int KUNPENG_PMU::PerfSampler::MapPerfAttr() { struct perf_event_attr attr; - if (memset_s(&attr, MAX_ATTR_SIZE, 0, sizeof(attr)) != EOK) { - return UNKNOWN_ERROR; - } + memset(&attr, 0, sizeof(attr)); attr.type = this->evt->type; attr.config = this->evt->config; attr.size = sizeof(struct perf_event_attr); diff --git a/pmu/spe.cpp b/pmu/spe.cpp index 44bdd34..1b27da6 100644 --- a/pmu/spe.cpp +++ b/pmu/spe.cpp @@ -20,10 +20,8 @@ #include #include #include -#include "securec.h" #include "pmu_event.h" #include "arm_spe_decoder.h" -#include "profile.h" #include "process_map.h" #include "log.h" #include "pcerr.h" @@ -149,7 +147,7 @@ static void CoreSpeClose(struct SpeCoreContext *ctx, struct SpeContext *speCtx) munmap(ctx->dummyMpage, speCtx->dummyMmapSize); } - memset_s(ctx, sizeof(*ctx), 0, sizeof(*ctx)); + memset(ctx, 0, sizeof(*ctx)); } static int CoreSpeOpenFailed(struct SpeCoreContext **ctx, struct SpeContext *speCtx) diff --git a/pmu/spe.h b/pmu/spe.h index 23f39a0..dc1f5c5 100644 --- a/pmu/spe.h +++ b/pmu/spe.h @@ -223,4 +223,4 @@ private: std::unordered_map> &procMap; }; -#endif \ No newline at end of file +#endif diff --git a/symbol/CMakeLists.txt b/symbol/CMakeLists.txt index 0338a95..2e97aac 100644 --- a/symbol/CMakeLists.txt +++ b/symbol/CMakeLists.txt @@ -15,8 +15,5 @@ include_directories(${SYMBOL_FILE_DIR}) include_directories(${INCLUDE_DIR}) message(${THIRD_PARTY}/elfin-parser/elf) -include_directories(${THIRD_PARTY}/json/single_include/nlohmann) -include_directories(${THIRD_PARTY}/huawei_secure_c/include) - ADD_LIBRARY(sym SHARED ${SYMBOL_SRC}) -target_link_libraries(sym elf_static dwarf_static securec pthread) +target_link_libraries(sym elf_static dwarf_static pthread) diff --git a/symbol/name_resolve.cpp b/symbol/name_resolve.cpp index 204b392..ec7b3a0 100644 --- a/symbol/name_resolve.cpp +++ b/symbol/name_resolve.cpp @@ -17,7 +17,7 @@ #include #include -char* CppNameDemangle(const char* abiName) +char* CppNamedDemangle(const char* abiName) { int status; char* name = abi::__cxa_demangle(abiName, nullptr, nullptr, &status); diff --git a/symbol/name_resolve.h b/symbol/name_resolve.h index 70169ba..09c6baf 100644 --- a/symbol/name_resolve.h +++ b/symbol/name_resolve.h @@ -20,9 +20,9 @@ extern "C" { #endif /** For further implementation such as support for python, rust or java name * demangel, APIs should be implemented here */ -char* CppNamedDemangel(const char* abiName); +char* CppNamedDemangle(const char* abiName); #ifdef __cpusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/symbol/symbol_resolve.cpp b/symbol/symbol_resolve.cpp index 8b8ba2c..3a1b22d 100644 --- a/symbol/symbol_resolve.cpp +++ b/symbol/symbol_resolve.cpp @@ -23,7 +23,6 @@ #include #include #include -#include "securec.h" #include "name_resolve.h" #include "pcerr.h" #include "symbol_resolve.h" @@ -85,10 +84,7 @@ namespace { static inline char* InitChar(int len) { char* str = new char[len + 1]; - auto ret = memset_s(str, len + 1, 0, len + 1); - if (ret != EOK) { - return nullptr; - } + memset(str, 0, len + 1); if (str == nullptr) { return nullptr; } @@ -108,9 +104,9 @@ namespace { } std::shared_ptr data = std::make_shared(); char modNameChar[MAX_LINUX_MODULE_LEN]; - if (!sscanf_s(line, "%lx-%lx %s %s %s %s %s", - &data->start, &data->end, mode, MODE_LEN, offset, OFFSET_LEN, dev, - DEV_LEN, inode, INODE_LEN, modNameChar, MODULE_NAME_LEN)) { + if (sscanf(line, "%lx-%lx %s %s %s %s %s", + &data->start, &data->end, mode, offset,dev, + inode, modNameChar) == EOF) { continue; } data->moduleName = modNameChar; @@ -157,9 +153,9 @@ namespace { stackAsm->funcName = InitChar(MAX_LINE_LENGTH); stackAsm->asmCode = nullptr; stackAsm->next = nullptr; - int ret = sscanf_s(line.c_str(), "%llx %s %*s %*s %llx", &stackAsm->funcStartAddr, stackAsm->funcName, - MAX_LINE_LENGTH, &stackAsm->functFileOffset); - if (ret <= 0) { + int ret = sscanf(line.c_str(), "%llx %s %*s %*s %llx", &stackAsm->funcStartAddr, stackAsm->funcName, + &stackAsm->functFileOffset); + if (ret == EOF) { free(stackAsm->funcName); stackAsm->funcName = nullptr; free(stackAsm); @@ -174,7 +170,7 @@ namespace { static bool MatchFileName(const std::string& line, std::string& fileName, unsigned int& lineNum) { char startStr[MAX_LINE_LENGTH + 1]; - int ret = sscanf_s(line.c_str(), "%s", startStr, MAX_LINE_LENGTH); + int ret = sscanf(line.c_str(), "%s", startStr); if (ret < 0) { return false; } @@ -209,8 +205,8 @@ namespace { } size_t tailLen = line.find("\n") != std::string::npos ? line.find("\n") : strlen(line.c_str()); char startStr[MAX_LINE_LENGTH + 1]; - int ret = sscanf_s(line.c_str(), "%*s %s", startStr, MAX_LINE_LENGTH); - if (ret < 0) { + int ret = sscanf(line.c_str(), "%*s %s", startStr); + if (ret == EOF) { delete asmCode; return nullptr; } @@ -220,17 +216,9 @@ namespace { codeStr.erase(0, codeStr.find_first_not_of("\t")); } asmCode->code = InitChar(MAX_LINE_LENGTH); - if (strcpy_s(asmCode->code, MAX_LINE_LENGTH, codeStr.c_str()) != EOK) { - delete[] asmCode->code; - delete[] asmCode; - return nullptr; - } + strcpy(asmCode->code, codeStr.c_str()); asmCode->fileName = InitChar(MAX_LINUX_FILE_NAME); - if (strcpy_s(asmCode->fileName, MAX_LINUX_FILE_NAME, srcFileName.c_str()) != EOK) { - delete[] asmCode->fileName; - delete[] asmCode; - return nullptr; - } + strcpy(asmCode->fileName, srcFileName.c_str()); asmCode->lineNum = lineNum; return asmCode; } @@ -402,7 +390,7 @@ int SymbolResolve::RecordModule(int pid, RecordModuleType recordModuleType) return 0; } char mapFile[MAP_LEN]; - if (!snprintf_s(mapFile, MAP_LEN + 1, MAP_LEN, "/proc/%d/maps", pid)) { + if (snprintf(mapFile, MAP_LEN, "/proc/%d/maps", pid) < 0) { moduleSafeHandler.releaseLock(pid); return LIBSYM_ERR_SNPRINF_OPERATE_FAILED; } @@ -442,7 +430,7 @@ int SymbolResolve::UpdateModule(int pid) } // Get memory maps of pid. char mapFile[MAP_LEN]; - if (!snprintf_s(mapFile, MAP_LEN + 1, MAP_LEN, "/proc/%d/maps", pid)) { + if (snprintf(mapFile, MAP_LEN, "/proc/%d/maps", pid) < 0) { moduleSafeHandler.releaseLock(pid); return LIBSYM_ERR_SNPRINF_OPERATE_FAILED; } @@ -652,26 +640,17 @@ void SymbolResolve::SearchElfInfo( if (start == end && elfVec[start].start <= addr && elfVec[start].end >= addr) { *offset = addr - elfVec[start].start; symbol->codeMapEndAddr = elfVec[start].end; - char* name = CppNameDemangle(elfVec[start].symbolName.c_str()); + char* name = CppNamedDemangle(elfVec[start].symbolName.c_str()); if (name) { - if (strcpy_s(symbol->symbolName, MAX_LINUX_MODULE_LEN, name) != EOK) { - delete[] symbol->symbolName; - symbol->symbolName = nullptr; - } + strcpy(symbol->symbolName, name); free(name); name = nullptr; return; } - if (strcpy_s(symbol->symbolName, MAX_LINUX_MODULE_LEN, elfVec[start].symbolName.c_str()) != EOK) { - delete[] symbol->symbolName; - symbol->symbolName = nullptr; - } + strcpy(symbol->symbolName, elfVec[start].symbolName.c_str()); return; } - if (strcpy_s(symbol->symbolName, MAX_LINUX_MODULE_LEN, "UNKNOWN") != EOK) { - delete[] symbol->symbolName; - symbol->symbolName = nullptr; - } + strcpy(symbol->symbolName, "UNKNOWN"); return; } @@ -695,16 +674,10 @@ void SymbolResolve::SearchDwarfInfo( } if (findLine) { std::string fileName = dwarfFileArray.GetKeyByIndex(dwarfMap.fileIndex); - if (strcpy_s(symbol->fileName, MAX_LINUX_MODULE_LEN, fileName.c_str()) != EOK) { - delete[] symbol->fileName; - symbol->fileName = nullptr; - } + strcpy(symbol->fileName, fileName.c_str()); symbol->lineNum = dwarfMap.lineNum; } else { - if (strcpy_s(symbol->fileName, MAX_LINUX_MODULE_LEN, "Uknown") != EOK) { - delete[] symbol->fileName; - symbol->fileName = nullptr; - } + strcpy(symbol->fileName, "Uknown"); symbol->lineNum = 0; } } @@ -817,10 +790,7 @@ struct Symbol* SymbolResolve::MapUserAddr(int pid, unsigned long addr) symbol->fileName = InitChar(MAX_LINUX_FILE_NAME); symbol->addr = addr; symbol->offset = 0; - if (strcpy_s(symbol->module, MAX_LINUX_MODULE_LEN, module->moduleName.c_str()) != EOK) { - delete[] symbol->module; - symbol->module = nullptr; - } + strcpy(symbol->module, module->moduleName.c_str()); unsigned long addrToSearch = addr; if (this->elfMap.find(module->moduleName) != this->elfMap.end()) { // If the largest symbol in the elf symbol table is detected to be smaller than the searched symbol, subtraction @@ -882,27 +852,18 @@ int SymbolResolve::RecordKernel() char name[KERNEL_MODULE_LNE]; while (fgets(line, sizeof(line), kallsyms)) { - if (!sscanf_s(line, "%llx %c %s%*[^\n]\n", &addr, &mode, 1, name, KERNEL_MODULE_LNE)) { + if (sscanf(line, "%llx %c %s%*[^\n]\n", &addr, &mode, name) == EOF) { continue; } ssize_t nameLen = strlen(name); std::shared_ptr data = std::make_shared(); data->symbolName = InitChar(nameLen); - if (strcpy_s(data->symbolName, MAX_LINUX_MODULE_LEN, name) != EOK) { - delete[] data->symbolName; - data->symbolName = nullptr; - } + strcpy(data->symbolName, name); data->addr = addr; data->fileName = InitChar(KERNEL_NAME_LEN); - if (strcpy_s(data->fileName, MAX_LINUX_MODULE_LEN, "KERNEL") != EOK) { - delete[] data->fileName; - data->fileName = nullptr; - } + strcpy(data->fileName, "KERNEL"); data->module = InitChar(KERNEL_NAME_LEN); - if (strcpy_s(data->module, MAX_LINUX_MODULE_LEN, "KERNEL") != EOK) { - delete[] data->module; - data->module = nullptr; - } + strcpy(data->module, "KERNEL"); data->lineNum = 0; this->ksymArray.emplace_back(data); } @@ -969,10 +930,7 @@ struct Symbol* SymbolResolve::MapUserCodeAddr(const std::string& moduleName, uns symbol->addr = startAddr; unsigned long addrToSearch = startAddr; symbol->codeMapAddr = addrToSearch; - if (strcpy_s(symbol->module, MAX_LINUX_MODULE_LEN, moduleName.c_str()) != EOK) { - delete[] symbol->module; - symbol->module = nullptr; - } + strcpy(symbol->module, moduleName.c_str()); if (this->elfMap.find(moduleName) != this->elfMap.end()) { this->SearchElfInfo(this->elfMap.at(moduleName), addrToSearch, symbol, &symbol->offset); } @@ -1011,7 +969,7 @@ struct StackAsm* ReadAsmCodeFromPipe(FILE* pipe) std::string srcFileName = "unknown"; unsigned int srcLineNum = 0; while (!feof(pipe)) { - memset_s(line, lineLen, 0, lineLen); + memset(line, 0, lineLen); if (!fgets(line, lineLen, pipe)) { break; } @@ -1063,12 +1021,12 @@ struct StackAsm* SymbolResolve::MapAsmCodeStack( pcerr::New(LIBSYM_ERR_START_SMALLER_END, "libysm the end address must be greater than the start address"); return nullptr; } - if (!snprintf_s(startAddrStr, ADDR_LEN + 1, ADDR_LEN, "0x%lx", startAddr)) { + if (snprintf(startAddrStr, ADDR_LEN, "0x%lx", startAddr) < 0) { pcerr::New(LIBSYM_ERR_SNPRINF_OPERATE_FAILED, "libsym fails to execute snprintf"); return nullptr; } - if (!snprintf_s(endAddrStr, ADDR_LEN + 1, ADDR_LEN, "0x%lx", endAddr)) { + if (snprintf(endAddrStr, ADDR_LEN, "0x%lx", endAddr) < 0) { pcerr::New(LIBSYM_ERR_SNPRINF_OPERATE_FAILED, "libsym fails to execute snprintf"); return nullptr; } @@ -1103,4 +1061,4 @@ std::vector> SymbolResolve::FindDiffMaps( SymbolResolve* SymbolResolve::instance = nullptr; std::mutex SymbolResolve::mutex; -std::mutex SymbolResolve::kernelMutex; \ No newline at end of file +std::mutex SymbolResolve::kernelMutex; diff --git a/util/CMakeLists.txt b/util/CMakeLists.txt index 9e57c45..a59ba9d 100644 --- a/util/CMakeLists.txt +++ b/util/CMakeLists.txt @@ -19,4 +19,4 @@ include_directories(${THIRD_PARTY}/huawei_secure_c/include) add_library(profu STATIC ${UTIL_SRC}) target_compile_options(profu PUBLIC -fPIC) -target_link_libraries(profu securec cap numa) \ No newline at end of file +target_link_libraries(profu numa) diff --git a/util/cpu_map.cpp b/util/cpu_map.cpp index 4f2f901..4443fb8 100644 --- a/util/cpu_map.cpp +++ b/util/cpu_map.cpp @@ -19,7 +19,6 @@ #include #include #include -#include "securec.h" #include "common.h" #include "pcerr.h" #include "cpu_map.h" @@ -28,8 +27,8 @@ using namespace std; static const std::string CPU_TOPOLOGY_PACKAGE_ID = "/sys/bus/cpu/devices/cpu%d/topology/physical_package_id"; static const std::string MIDR_EL1 = "/sys/devices/system/cpu/cpu0/regs/identification/midr_el1"; -static const std::string KUNPENG920_ID = "0x00000000481fd010"; -static const std::string KUNPENG920B_ID = "0x00000000480fd020"; +static const std::string HIPA_ID = "0x00000000481fd010"; +static const std::string HIPB_ID = "0x00000000480fd020"; static constexpr int PATH_LEN = 256; static CHIP_TYPE g_chipType = CHIP_TYPE::UNDEFINED_TYPE; @@ -37,7 +36,7 @@ static CHIP_TYPE g_chipType = CHIP_TYPE::UNDEFINED_TYPE; static inline bool ReadCpuPackageId(int coreId, CpuTopology* cpuTopo) { char filename[PATH_LEN]; - if (snprintf_s(filename, PATH_LEN + 1, PATH_LEN, CPU_TOPOLOGY_PACKAGE_ID.c_str(), coreId) < 0) { + if (snprintf(filename, PATH_LEN, CPU_TOPOLOGY_PACKAGE_ID.c_str(), coreId) < 0) { return false; } std::string realPath = GetRealPath(filename); @@ -61,7 +60,7 @@ static inline bool ReadCpuPackageId(int coreId, CpuTopology* cpuTopo) struct CpuTopology* GetCpuTopology(int coreId) { auto cpuTopo = std::unique_ptr(new CpuTopology()); - memset_s(cpuTopo.get(), sizeof(CpuTopology), 0, sizeof(CpuTopology)); + memset(cpuTopo.get(), 0, sizeof(CpuTopology)); if (coreId == -1) { cpuTopo->coreId = coreId; cpuTopo->numaId = -1; @@ -85,10 +84,10 @@ bool InitCpuType() std::ifstream cpuFile(MIDR_EL1); std::string cpuId; cpuFile >> cpuId; - if (cpuId.compare(KUNPENG920_ID) == 0) { - g_chipType = CHIP_TYPE::KUNPENG_920; - } else if (cpuId.compare(KUNPENG920B_ID) == 0) { - g_chipType = CHIP_TYPE::KUNPENG_920B; + if (cpuId.compare(HIPA_ID) == 0) { + g_chipType = CHIP_TYPE::HIPA; + } else if (cpuId.compare(HIPB_ID) == 0) { + g_chipType = CHIP_TYPE::HIPB; } else { pcerr::New(LIBPERF_ERR_CHIP_TYPE_INVALID, "invalid chip type"); return false; @@ -102,4 +101,4 @@ CHIP_TYPE GetCpuType() return UNDEFINED_TYPE; } return g_chipType; -} \ No newline at end of file +} diff --git a/util/cpu_map.h b/util/cpu_map.h index b7f810c..016fadb 100644 --- a/util/cpu_map.h +++ b/util/cpu_map.h @@ -22,8 +22,8 @@ extern "C" { enum CHIP_TYPE { UNDEFINED_TYPE = 0, - KUNPENG_920 = 1, - KUNPENG_920B = 2, + HIPA = 1, + HIPB = 2, }; struct CpuTopology { diff --git a/util/linked_list.h b/util/linked_list.h index 3b3f034..2a2cd50 100644 --- a/util/linked_list.h +++ b/util/linked_list.h @@ -17,20 +17,16 @@ #include #include #include -#include "securec.h" // Function to create a new node template ListNode* CreateNode() { ListNode* newNode = (ListNode*)malloc(sizeof(ListNode)); - auto ret = memset_s(newNode, sizeof(ListNode), 0, sizeof(ListNode)); - if (ret != EOK) { - return nullptr; - } if (newNode == nullptr) { return nullptr; } + memset(newNode, 0, sizeof(ListNode)); newNode->next = nullptr; return newNode; @@ -73,4 +69,4 @@ void FreeList(ListNode** head) } *head = nullptr; // Ensure the head is set to nullptr after freeing all nodes } -#endif \ No newline at end of file +#endif diff --git a/util/process_map.cpp b/util/process_map.cpp index dcbaa91..96da01f 100644 --- a/util/process_map.cpp +++ b/util/process_map.cpp @@ -20,7 +20,6 @@ #include #include #include -#include "securec.h" #include "common.h" #include "process_map.h" @@ -141,9 +140,7 @@ static char *GetComm(pid_t pid) if (comm == nullptr) { return nullptr; } - if (strcpy_s(comm, COMM_SIZE, commName.c_str()) != EOK) { - return nullptr; - } + strcpy(comm, commName.c_str()); return comm; } std::string filePath = "/proc/" + std::to_string(pid) + "/comm"; @@ -160,9 +157,7 @@ static char *GetComm(pid_t pid) if (comm == nullptr) { return nullptr; } - if (strcpy_s(comm, COMM_SIZE, commName.c_str()) != EOK) { - return nullptr; - } + strcpy(comm, commName.c_str()); return comm; } @@ -236,7 +231,7 @@ bool GetChildTidRecursive(const char *dirPath, int **childTidList, int *count) } char path[PATH_LEN]; - if (!snprintf_s(path, PATH_LEN + 1, sizeof(path), "%s/%s", dirPath, entry->d_name)) { + if (snprintf(path, sizeof(path), "%s/%s", dirPath, entry->d_name) < 0) { continue; } @@ -253,7 +248,7 @@ int *GetChildTid(int pid, int *numChild) { int *childTidList = nullptr; char dirPath[PATH_LEN]; - if (!snprintf_s(dirPath, PATH_LEN + 1, sizeof(dirPath), "/proc/%d/task", pid)) { + if (snprintf(dirPath, sizeof(dirPath), "/proc/%d/task", pid) < 0) { return nullptr; } *numChild = 0; -- Gitee From a66f97a54ca41add72066a0a5cf174006866a40f Mon Sep 17 00:00:00 2001 From: Junyi Ye <294572668@qq.com> Date: Wed, 10 Apr 2024 11:15:23 +0800 Subject: [PATCH 09/14] core event config --- pmu/pfm/core.cpp | 606 +++++++++++++++++++++++++++++++++++++++++++ pmu/pfm/pfm_name.cpp | 99 +++++++ pmu/pfm/pfm_name.h | 108 ++++++++ 3 files changed, 813 insertions(+) create mode 100644 pmu/pfm/core.cpp create mode 100644 pmu/pfm/pfm_name.cpp create mode 100644 pmu/pfm/pfm_name.h diff --git a/pmu/pfm/core.cpp b/pmu/pfm/core.cpp new file mode 100644 index 0000000..10980da --- /dev/null +++ b/pmu/pfm/core.cpp @@ -0,0 +1,606 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: core event config + ******************************************************************************/ +#include +#include +#include +#include "pmu.h" +#include "core.h" + +const std::unordered_map HIP_A_CORE_PMU_MAP{ + { + KUNPENG_PMU::HIP_A::CORE::BRANCH_MISSES, + { + PERF_TYPE_RAW, + 0x5, + KUNPENG_PMU::HIP_A::CORE::BRANCH_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::BUS_CYCLES, + { + PERF_TYPE_RAW, + 0x6, + KUNPENG_PMU::HIP_A::CORE::BUS_CYCLES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::CACHE_MISSES, + { + PERF_TYPE_RAW, + 0x3, + KUNPENG_PMU::HIP_A::CORE::CACHE_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::CACHE_REFERENCES, + { + PERF_TYPE_RAW, + 0x2, + KUNPENG_PMU::HIP_A::CORE::CACHE_REFERENCES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::CPU_CYCLES, + { + PERF_TYPE_RAW, + 0x11, + KUNPENG_PMU::HIP_A::CORE::CPU_CYCLES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::CYCLES, + { + PERF_TYPE_RAW, + 0x11, + KUNPENG_PMU::HIP_A::CORE::CYCLES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::INSTRUCTIONS, + { + PERF_TYPE_RAW, + 0x1, + KUNPENG_PMU::HIP_A::CORE::INSTRUCTIONS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::STALLED_CYCLES_BACKEND, + { + PERF_TYPE_RAW, + 0x8, + KUNPENG_PMU::HIP_A::CORE::STALLED_CYCLES_BACKEND + } + }, + { + KUNPENG_PMU::HIP_A::CORE::STALLED_CYCLES_FRONTEND, + { + PERF_TYPE_RAW, + 0x7, + KUNPENG_PMU::HIP_A::CORE::STALLED_CYCLES_FRONTEND + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1_DCACHE_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10000, + KUNPENG_PMU::HIP_A::CORE::L1_DCACHE_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::IDLE_CYCLES_BACKEND, + { + PERF_TYPE_RAW, + 0x8, + KUNPENG_PMU::HIP_A::CORE::IDLE_CYCLES_BACKEND + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1_ICACHE_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10001, + KUNPENG_PMU::HIP_A::CORE::L1_ICACHE_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::IDLE_CYCLES_FRONTEND, + { + PERF_TYPE_RAW, + 0x7, + KUNPENG_PMU::HIP_A::CORE::IDLE_CYCLES_FRONTEND + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1_ICACHE_LOADS, + { + PERF_TYPE_RAW, + 0x1, + KUNPENG_PMU::HIP_A::CORE::L1_ICACHE_LOADS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::LLC_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10002, + KUNPENG_PMU::HIP_A::CORE::LLC_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::LLC_LOADS, + { + PERF_TYPE_RAW, + 0x2, + KUNPENG_PMU::HIP_A::CORE::LLC_LOADS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::BRANCH_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10005, + KUNPENG_PMU::HIP_A::CORE::BRANCH_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::BRANCH_LOADS, + { + PERF_TYPE_RAW, + 0x5, + KUNPENG_PMU::HIP_A::CORE::BRANCH_LOADS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::DTLB_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10003, + KUNPENG_PMU::HIP_A::CORE::DTLB_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::DTLB_LOADS, + { + PERF_TYPE_RAW, + 0x3, + KUNPENG_PMU::HIP_A::CORE::DTLB_LOADS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::ITLB_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10004, + KUNPENG_PMU::HIP_A::CORE::ITLB_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_A::CORE::ITLB_LOADS, + { + PERF_TYPE_RAW, + 0x4, + KUNPENG_PMU::HIP_A::CORE::ITLB_LOADS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_RD, + { + PERF_TYPE_RAW, + 0x40, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_RD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WR, + { + PERF_TYPE_RAW, + 0x41, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WR + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_REFILL_RD, + { + PERF_TYPE_RAW, + 0x42, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_REFILL_RD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_REFILL_WR, + { + PERF_TYPE_RAW, + 0x43, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_REFILL_WR + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WB_VICTIM, + { + PERF_TYPE_RAW, + 0x46, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WB_VICTIM + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WB_CLEAN, + { + PERF_TYPE_RAW, + 0x47, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WB_CLEAN + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_INVAL, + { + PERF_TYPE_RAW, + 0x48, + KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_INVAL + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_REFILL_RD, + { + PERF_TYPE_RAW, + 0x4c, + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_REFILL_RD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_REFILL_WR, + { + PERF_TYPE_RAW, + 0x4d, + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_REFILL_WR + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_RD, + { + PERF_TYPE_RAW, + 0x4e, + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_RD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_WR, + { + PERF_TYPE_RAW, + 0x4f, + KUNPENG_PMU::HIP_A::CORE::L1D_TLB_WR + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_RD, + { + PERF_TYPE_RAW, + 0x50, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_RD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WR, + { + PERF_TYPE_RAW, + 0x51, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WR + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_REFILL_RD, + { + PERF_TYPE_RAW, + 0x52, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_REFILL_RD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_REFILL_WR, + { + PERF_TYPE_RAW, + 0x53, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_REFILL_WR + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WB_VICTIM, + { + PERF_TYPE_RAW, + 0x56, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WB_VICTIM + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WB_CLEAN, + { + PERF_TYPE_RAW, + 0x57, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WB_CLEAN + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_INVAL, + { + PERF_TYPE_RAW, + 0x58, + KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_INVAL + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1I_CACHE_PRF, + { + PERF_TYPE_RAW, + 0x102e, + KUNPENG_PMU::HIP_A::CORE::L1I_CACHE_PRF + } + }, + { + KUNPENG_PMU::HIP_A::CORE::L1I_CACHE_PRF_REFILL, + { + PERF_TYPE_RAW, + 0x102f, + KUNPENG_PMU::HIP_A::CORE::L1I_CACHE_PRF_REFILL + } + }, + { + KUNPENG_PMU::HIP_A::CORE::IQ_IS_EMPTY, + { + PERF_TYPE_RAW, + 0x1043, + KUNPENG_PMU::HIP_A::CORE::IQ_IS_EMPTY + } + }, + { + KUNPENG_PMU::HIP_A::CORE::IF_IS_STALL, + { + PERF_TYPE_RAW, + 0x1044, + KUNPENG_PMU::HIP_A::CORE::IF_IS_STALL + } + }, + { + KUNPENG_PMU::HIP_A::CORE::FETCH_BUBBLE, + { + PERF_TYPE_RAW, + 0x2014, + KUNPENG_PMU::HIP_A::CORE::FETCH_BUBBLE + } + }, + { + KUNPENG_PMU::HIP_A::CORE::PRF_REQ, + { + PERF_TYPE_RAW, + 0x6013, + KUNPENG_PMU::HIP_A::CORE::PRF_REQ + } + }, + { + KUNPENG_PMU::HIP_A::CORE::HIT_ON_PRF, + { + PERF_TYPE_RAW, + 0x6014, + KUNPENG_PMU::HIP_A::CORE::HIT_ON_PRF + } + }, + { + KUNPENG_PMU::HIP_A::CORE::EXE_STALL_CYCLE, + { + PERF_TYPE_RAW, + 0x7001, + KUNPENG_PMU::HIP_A::CORE::EXE_STALL_CYCLE + } + }, + { + KUNPENG_PMU::HIP_A::CORE::MEM_STALL_ANYLOAD, + { + PERF_TYPE_RAW, + 0x7004, + KUNPENG_PMU::HIP_A::CORE::MEM_STALL_ANYLOAD + } + }, + { + KUNPENG_PMU::HIP_A::CORE::MEM_STALL_L1MISS, + { + PERF_TYPE_RAW, + 0x7006, + KUNPENG_PMU::HIP_A::CORE::MEM_STALL_L1MISS + } + }, + { + KUNPENG_PMU::HIP_A::CORE::MEM_STALL_L2MISS, + { + PERF_TYPE_RAW, + 0x7007, + KUNPENG_PMU::HIP_A::CORE::MEM_STALL_L2MISS + } + }, +}; + +const std::unordered_map HIP_B_CORE_PMU_MAP{ + { + KUNPENG_PMU::HIP_B::CORE::BRANCH_MISSES, + { + PERF_TYPE_RAW, + 0x5, + KUNPENG_PMU::HIP_B::CORE::BRANCH_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::CACHE_MISSES, + { + PERF_TYPE_RAW, + 0x3, + KUNPENG_PMU::HIP_B::CORE::CACHE_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::CACHE_REFERENCES, + { + PERF_TYPE_RAW, + 0x2, + KUNPENG_PMU::HIP_B::CORE::CACHE_REFERENCES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::CPU_CYCLES, + { + PERF_TYPE_RAW, + 0x11, + KUNPENG_PMU::HIP_B::CORE::CPU_CYCLES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::CYCLES, + { + PERF_TYPE_RAW, + 0x11, + KUNPENG_PMU::HIP_B::CORE::CYCLES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::INSTRUCTIONS, + { + PERF_TYPE_RAW, + 0x1, + KUNPENG_PMU::HIP_B::CORE::INSTRUCTIONS + } + }, + { + KUNPENG_PMU::HIP_B::CORE::STALLED_CYCLES_BACKEND, + { + PERF_TYPE_RAW, + 0x8, + KUNPENG_PMU::HIP_B::CORE::STALLED_CYCLES_BACKEND + } + }, + { + KUNPENG_PMU::HIP_B::CORE::STALLED_CYCLES_FRONTEND, + { + PERF_TYPE_RAW, + 0x7, + KUNPENG_PMU::HIP_B::CORE::STALLED_CYCLES_FRONTEND + } + }, + { + KUNPENG_PMU::HIP_B::CORE::L1_DCACHE_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10000, + KUNPENG_PMU::HIP_B::CORE::L1_DCACHE_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::IDLE_CYCLES_BACKEND, + { + PERF_TYPE_RAW, + 0x8, + KUNPENG_PMU::HIP_B::CORE::IDLE_CYCLES_BACKEND + } + }, + { + KUNPENG_PMU::HIP_B::CORE::L1_ICACHE_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10001, + KUNPENG_PMU::HIP_B::CORE::L1_ICACHE_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::IDLE_CYCLES_FRONTEND, + { + PERF_TYPE_RAW, + 0x7, + KUNPENG_PMU::HIP_B::CORE::IDLE_CYCLES_FRONTEND + } + }, + { + KUNPENG_PMU::HIP_B::CORE::L1_ICACHE_LOADS, + { + PERF_TYPE_RAW, + 0x1, + KUNPENG_PMU::HIP_B::CORE::L1_ICACHE_LOADS + } + }, + { + KUNPENG_PMU::HIP_B::CORE::LLC_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10002, + KUNPENG_PMU::HIP_B::CORE::LLC_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::LLC_LOADS, + { + PERF_TYPE_RAW, + 0x2, + KUNPENG_PMU::HIP_B::CORE::LLC_LOADS + } + }, + { + KUNPENG_PMU::HIP_B::CORE::BRANCH_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10005, + KUNPENG_PMU::HIP_B::CORE::BRANCH_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::BRANCH_LOADS, + { + PERF_TYPE_RAW, + 0x5, + KUNPENG_PMU::HIP_B::CORE::BRANCH_LOADS + } + }, + { + KUNPENG_PMU::HIP_B::CORE::DTLB_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10003, + KUNPENG_PMU::HIP_B::CORE::DTLB_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::DTLB_LOADS, + { + PERF_TYPE_RAW, + 0x3, + KUNPENG_PMU::HIP_B::CORE::DTLB_LOADS + } + }, + { + KUNPENG_PMU::HIP_B::CORE::ITLB_LOAD_MISSES, + { + PERF_TYPE_RAW, + 0x10004, + KUNPENG_PMU::HIP_B::CORE::ITLB_LOAD_MISSES + } + }, + { + KUNPENG_PMU::HIP_B::CORE::ITLB_LOADS, + { + PERF_TYPE_RAW, + 0x4, + KUNPENG_PMU::HIP_B::CORE::ITLB_LOADS + } + }, +}; + +const KUNPENG_PMU::CORE_EVT_MAP KUNPENG_PMU::CORE_EVENT_MAP = { + {CHIP_TYPE::KUNPENG_HIP_A, HIP_A_CORE_PMU_MAP}, + {CHIP_TYPE::KUNPENG_HIP_B, HIP_B_CORE_PMU_MAP}, +}; \ No newline at end of file diff --git a/pmu/pfm/pfm_name.cpp b/pmu/pfm/pfm_name.cpp new file mode 100644 index 0000000..a587dc4 --- /dev/null +++ b/pmu/pfm/pfm_name.cpp @@ -0,0 +1,99 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: core event name definition + ******************************************************************************/ +#include +#include +#include +#include +#include "pfm_name.h" + +/** + * CORE events for HIP_A + */ +const char* KUNPENG_PMU::HIP_A::CORE::BRANCH_MISSES = "branch-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::BUS_CYCLES = "bus-cycles"; +const char* KUNPENG_PMU::HIP_A::CORE::CACHE_MISSES = "cache-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::CACHE_REFERENCES = "cache-references"; +const char* KUNPENG_PMU::HIP_A::CORE::CPU_CYCLES = "cpu-cycles"; +const char* KUNPENG_PMU::HIP_A::CORE::CYCLES = "cycles"; +const char* KUNPENG_PMU::HIP_A::CORE::INSTRUCTIONS = "instructions"; +const char* KUNPENG_PMU::HIP_A::CORE::STALLED_CYCLES_BACKEND = "stalled-cycles-backend"; +const char* KUNPENG_PMU::HIP_A::CORE::STALLED_CYCLES_FRONTEND = "stalled-cycles-frontend"; +const char* KUNPENG_PMU::HIP_A::CORE::L1_DCACHE_LOAD_MISSES = "l1-dcache-load-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::IDLE_CYCLES_BACKEND = "idle-cycles-backend"; +const char* KUNPENG_PMU::HIP_A::CORE::L1_ICACHE_LOAD_MISSES = "l1-icache-load-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::IDLE_CYCLES_FRONTEND = "idle-cycles-frontend"; +const char* KUNPENG_PMU::HIP_A::CORE::L1_ICACHE_LOADS = "l1-icache-loads"; +const char* KUNPENG_PMU::HIP_A::CORE::LLC_LOAD_MISSES = "llc-load-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::LLC_LOADS = "llc-loads"; +const char* KUNPENG_PMU::HIP_A::CORE::BRANCH_LOAD_MISSES = "branch-load-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::BRANCH_LOADS = "branch-loads"; +const char* KUNPENG_PMU::HIP_A::CORE::DTLB_LOAD_MISSES = "dtlb-load-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::DTLB_LOADS = "dtlb-loads"; +const char* KUNPENG_PMU::HIP_A::CORE::ITLB_LOAD_MISSES = "itlb-load-misses"; +const char* KUNPENG_PMU::HIP_A::CORE::ITLB_LOADS = "itlb-loads"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_RD = "l1d-cache-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WR = "l1d-cache-wr"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_REFILL_RD = "l1d-cache-refill-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_REFILL_WR = "l1d-cache-refill-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WB_VICTIM = "l1d-cache-wb-victim"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_WB_CLEAN = "l1d-cache-wb-clean"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_CACHE_INVAL = "l1d-cache-inval"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_TLB_REFILL_RD = "l1d-tlb-refill-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_TLB_REFILL_WR = "l1d-tlb-refill-wr"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_TLB_RD = "l1d-tlb-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L1D_TLB_WR = "l1d-tlb-wr"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_RD = "l2d-cache-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WR = "l2d-cache-wr"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_REFILL_RD = "l2d-cache-refill-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_REFILL_WR = "l2d-cache-refill-rd"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WB_VICTIM = "l2d-cache-wb-victim"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_WB_CLEAN = "l2d-cache-wb-clean"; +const char* KUNPENG_PMU::HIP_A::CORE::L2D_CACHE_INVAL = "l2d-cache-inval"; +const char* KUNPENG_PMU::HIP_A::CORE::L1I_CACHE_PRF = "l1i-cache-prf"; +const char* KUNPENG_PMU::HIP_A::CORE::L1I_CACHE_PRF_REFILL = "l1i-cache-prf-refill"; +const char* KUNPENG_PMU::HIP_A::CORE::IQ_IS_EMPTY = "iq-is-empty"; +const char* KUNPENG_PMU::HIP_A::CORE::IF_IS_STALL = "if-is-stall"; +const char* KUNPENG_PMU::HIP_A::CORE::FETCH_BUBBLE = "fetch-bubble"; +const char* KUNPENG_PMU::HIP_A::CORE::PRF_REQ = "prf-req"; +const char* KUNPENG_PMU::HIP_A::CORE::HIT_ON_PRF = "hit-on-prf"; +const char* KUNPENG_PMU::HIP_A::CORE::EXE_STALL_CYCLE = "exe-stall-cycle"; +const char* KUNPENG_PMU::HIP_A::CORE::MEM_STALL_ANYLOAD = "mem-stall-anyload"; +const char* KUNPENG_PMU::HIP_A::CORE::MEM_STALL_L1MISS = "mem-stall-l1miss"; +const char* KUNPENG_PMU::HIP_A::CORE::MEM_STALL_L2MISS = "mem-stall-l2miss"; + +/** + * CORE events for HIP_B + */ +const char* KUNPENG_PMU::HIP_B::CORE::BRANCH_MISSES = "branch-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::CACHE_MISSES = "cache-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::CACHE_REFERENCES = "cache-references"; +const char* KUNPENG_PMU::HIP_B::CORE::CPU_CYCLES = "cpu-cycles"; +const char* KUNPENG_PMU::HIP_B::CORE::CYCLES = "cycles"; +const char* KUNPENG_PMU::HIP_B::CORE::INSTRUCTIONS = "instructions"; +const char* KUNPENG_PMU::HIP_B::CORE::STALLED_CYCLES_BACKEND = "stalled-cycles-backend"; +const char* KUNPENG_PMU::HIP_B::CORE::STALLED_CYCLES_FRONTEND = "stalled-cycles-frontend"; +const char* KUNPENG_PMU::HIP_B::CORE::L1_DCACHE_LOAD_MISSES = "l1-dcache-load-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::IDLE_CYCLES_BACKEND = "idle-cycles-backend"; +const char* KUNPENG_PMU::HIP_B::CORE::L1_ICACHE_LOAD_MISSES = "l1-icache-load-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::IDLE_CYCLES_FRONTEND = "idle-cycles-frontend"; +const char* KUNPENG_PMU::HIP_B::CORE::L1_ICACHE_LOADS = "l1-icache-loads"; +const char* KUNPENG_PMU::HIP_B::CORE::LLC_LOAD_MISSES = "llc-load-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::LLC_LOADS = "llc-loads"; +const char* KUNPENG_PMU::HIP_B::CORE::BRANCH_LOAD_MISSES = "branch-load-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::BRANCH_LOADS = "branch-loads"; +const char* KUNPENG_PMU::HIP_B::CORE::DTLB_LOAD_MISSES = "dtlb-load-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::DTLB_LOADS = "dtlb-loads"; +const char* KUNPENG_PMU::HIP_B::CORE::ITLB_LOAD_MISSES = "itlb-load-misses"; +const char* KUNPENG_PMU::HIP_B::CORE::ITLB_LOADS = "itlb-loads"; \ No newline at end of file diff --git a/pmu/pfm/pfm_name.h b/pmu/pfm/pfm_name.h new file mode 100644 index 0000000..dab16b4 --- /dev/null +++ b/pmu/pfm/pfm_name.h @@ -0,0 +1,108 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved. + * gala-gopher 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: core event name declination + ******************************************************************************/ +#ifndef PFM_NAME_H +#define PFM_NAME_H +#include +#include +#include +#include + +namespace KUNPENG_PMU { +namespace HIP_A { +namespace CORE { +extern const char* BRANCH_MISSES; +extern const char* BUS_CYCLES; +extern const char* CACHE_MISSES; +extern const char* CACHE_REFERENCES; +extern const char* CPU_CYCLES; +extern const char* CYCLES; +extern const char* INSTRUCTIONS; +extern const char* STALLED_CYCLES_BACKEND; +extern const char* STALLED_CYCLES_FRONTEND; +extern const char* L1_DCACHE_LOAD_MISSES; +extern const char* IDLE_CYCLES_BACKEND; +extern const char* L1_ICACHE_LOAD_MISSES; +extern const char* IDLE_CYCLES_FRONTEND; +extern const char* L1_ICACHE_LOADS; +extern const char* LLC_LOAD_MISSES; +extern const char* LLC_LOADS; +extern const char* BRANCH_LOAD_MISSES; +extern const char* BRANCH_LOADS; +extern const char* DTLB_LOAD_MISSES; +extern const char* DTLB_LOADS; +extern const char* ITLB_LOAD_MISSES; +extern const char* ITLB_LOADS; +extern const char* L1D_CACHE_RD; +extern const char* L1D_CACHE_WR; +extern const char* L1D_CACHE_REFILL_RD; +extern const char* L1D_CACHE_REFILL_WR; +extern const char* L1D_CACHE_WB_VICTIM; +extern const char* L1D_CACHE_WB_CLEAN; +extern const char* L1D_CACHE_INVAL; +extern const char* L1D_TLB_REFILL_RD; +extern const char* L1D_TLB_REFILL_WR; +extern const char* L1D_TLB_RD; +extern const char* L1D_TLB_WR; +extern const char* L2D_CACHE_RD; +extern const char* L2D_CACHE_WR; +extern const char* L2D_CACHE_REFILL_RD; +extern const char* L2D_CACHE_REFILL_WR; +extern const char* L2D_CACHE_WB_VICTIM; +extern const char* L2D_CACHE_WB_CLEAN; +extern const char* L2D_CACHE_INVAL; +extern const char* L1I_CACHE_PRF; +extern const char* L1I_CACHE_PRF_REFILL; +extern const char* IQ_IS_EMPTY; +extern const char* IF_IS_STALL; +extern const char* FETCH_BUBBLE; +extern const char* PRF_REQ; +extern const char* HIT_ON_PRF; +extern const char* EXE_STALL_CYCLE; +extern const char* MEM_STALL_ANYLOAD; +extern const char* MEM_STALL_L1MISS; +extern const char* MEM_STALL_L2MISS; +} // namespace CORE + +} // namespace HIP_A + +namespace HIP_B { +namespace CORE { +extern const char* BRANCH_MISSES; +extern const char* CACHE_MISSES; +extern const char* CACHE_REFERENCES; +extern const char* CPU_CYCLES; +extern const char* CYCLES; +extern const char* INSTRUCTIONS; +extern const char* STALLED_CYCLES_BACKEND; +extern const char* STALLED_CYCLES_FRONTEND; +extern const char* L1_DCACHE_LOAD_MISSES; +extern const char* IDLE_CYCLES_BACKEND; +extern const char* L1_ICACHE_LOAD_MISSES; +extern const char* IDLE_CYCLES_FRONTEND; +extern const char* L1_ICACHE_LOADS; +extern const char* LLC_LOAD_MISSES; +extern const char* LLC_LOADS; +extern const char* BRANCH_LOAD_MISSES; +extern const char* BRANCH_LOADS; +extern const char* DTLB_LOAD_MISSES; +extern const char* DTLB_LOADS; +extern const char* ITLB_LOAD_MISSES; +extern const char* ITLB_LOADS; +} // namespace CORE + +} // namespace HIP_B + +} // namespace KUNPENG_PMU +#endif \ No newline at end of file -- Gitee From 4de2c1c2eb16228ee07e1792367fb0a52e5ab279 Mon Sep 17 00:00:00 2001 From: Junyi Ye <294572668@qq.com> Date: Wed, 10 Apr 2024 11:54:57 +0800 Subject: [PATCH 10/14] modification for view comments --- pmu/pfm/pfm.cpp | 38 +------------------------------------- pmu/pfm/trace.cpp | 19 ++++++++++++++++++- pmu/pfm/trace.h | 2 +- pmu/pfm/uncore.cpp | 25 ++++++++++++++++++++++++- pmu/pfm/uncore.h | 4 +--- 5 files changed, 45 insertions(+), 43 deletions(-) diff --git a/pmu/pfm/pfm.cpp b/pmu/pfm/pfm.cpp index 2757120..1f8ea3f 100644 --- a/pmu/pfm/pfm.cpp +++ b/pmu/pfm/pfm.cpp @@ -48,6 +48,7 @@ static struct PmuEvt* ConstructPmuEvtFromCore(KUNPENG_PMU::CoreConfig config, in pmuEvtPtr->type = config.type; pmuEvtPtr->pmuType = CORE_TYPE; pmuEvtPtr->collectType = collectType; + pmuEvtPtr->cpumask = -1; return std::move(pmuEvtPtr); } @@ -60,34 +61,6 @@ static struct PmuEvt* GetCoreEvent(const char* pmuName, int collectType) : nullptr; } -static struct PmuEvt* GetUncoreEvent(const char* pmuName, int collectType) -{ - int64_t config = GetUncoreEventConfig(pmuName); - if (config == -1) { - return nullptr; - } - auto* pmuEvtPtr = new PmuEvt; - pmuEvtPtr->config = config; - pmuEvtPtr->name = pmuName; - pmuEvtPtr->pmuType = UNCORE_TYPE; - pmuEvtPtr->collectType = collectType; - return pmuEvtPtr; -} - -static struct PmuEvt* GetKernelTraceEvent(const char* pmuName, int collectType) -{ - int64_t config = GetTraceEventConfig(pmuName); - if (config == -1) { - return nullptr; - } - auto* pmuEvtPtr = new PmuEvt; - pmuEvtPtr->config = config; - pmuEvtPtr->name = pmuName; - pmuEvtPtr->type = PERF_TYPE_TRACEPOINT; - pmuEvtPtr->collectType = collectType; - return pmuEvtPtr; -} - static int GetSpeType(void) { constexpr char* speTypePath = "/sys/devices/arm_spe_0/type"; @@ -169,15 +142,6 @@ struct PmuEvt* PfmGetPmuEvent(const char* pmuName, int collectType) if (evt == nullptr) { return evt; } - if (evt->pmuType == UNCORE_TYPE) { - // Fill fields for uncore devices. - auto err = FillUncoreFields(pmuName, evt); - if (err != SUCCESS) { - return nullptr; - } - } else if (evt != nullptr) { - evt->cpumask = -1; - } return evt; } diff --git a/pmu/pfm/trace.cpp b/pmu/pfm/trace.cpp index a4e7b87..9e2c77a 100644 --- a/pmu/pfm/trace.cpp +++ b/pmu/pfm/trace.cpp @@ -14,11 +14,13 @@ ******************************************************************************/ #include #include "common.h" +#include "pmu_event.h" #include "trace.h" using namespace std; +using namespace KUNPENG_PMU; -int64_t GetTraceEventConfig(const std::string &name) +static int64_t GetTraceEventConfig(const std::string &name) { size_t colon = name.find(':'); string systemName = name.substr(0, colon); @@ -38,3 +40,18 @@ int64_t GetTraceEventConfig(const std::string &name) return stoi(typeStr); } + +struct PmuEvt* GetKernelTraceEvent(const char* pmuName, int collectType) +{ + int64_t config = GetTraceEventConfig(pmuName); + if (config == -1) { + return nullptr; + } + auto* pmuEvtPtr = new PmuEvt; + pmuEvtPtr->config = config; + pmuEvtPtr->name = pmuName; + pmuEvtPtr->type = PERF_TYPE_TRACEPOINT; + pmuEvtPtr->collectType = collectType; + pmuEvtPtr->cpumask = -1; + return pmuEvtPtr; +} diff --git a/pmu/pfm/trace.h b/pmu/pfm/trace.h index eaa2d1e..55b5fd4 100644 --- a/pmu/pfm/trace.h +++ b/pmu/pfm/trace.h @@ -15,6 +15,6 @@ #ifndef TRACE_H #define TRACE_H -int64_t GetTraceEventConfig(const std::string &name); +struct PmuEvt* GetKernelTraceEvent(const char* pmuName, int collectType); #endif diff --git a/pmu/pfm/uncore.cpp b/pmu/pfm/uncore.cpp index 3db4a17..16257f3 100644 --- a/pmu/pfm/uncore.cpp +++ b/pmu/pfm/uncore.cpp @@ -15,9 +15,12 @@ #include #include "common.h" #include "pcerr.h" +#include "pfm_event.h" +#include "pmu_event.h" #include "uncore.h" using namespace std; +using namespace KUNPENG_PMU; static int GetDeviceType(const string &devName) { @@ -55,7 +58,7 @@ static int GetCpuMask(const string &devName) return stoi(maskStr); } -int64_t GetUncoreEventConfig(const char* pmuName) +static int64_t GetUncoreEventConfig(const char* pmuName) { int64_t config; string strName(pmuName); @@ -106,4 +109,24 @@ int FillUncoreFields(const char* pmuName, PmuEvt *evt) evt->cpumask = cpuMask; evt->name = pmuName; return SUCCESS; +} + +struct PmuEvt* GetUncoreEvent(const char* pmuName, int collectType) +{ + int64_t config = GetUncoreEventConfig(pmuName); + if (config == -1) { + return nullptr; + } + auto* pmuEvtPtr = new PmuEvt; + pmuEvtPtr->config = config; + pmuEvtPtr->name = pmuName; + pmuEvtPtr->pmuType = UNCORE_TYPE; + pmuEvtPtr->collectType = collectType; + + // Fill fields for uncore devices. + auto err = FillUncoreFields(pmuName, pmuEvtPtr); + if (err != SUCCESS) { + return nullptr; + } + return pmuEvtPtr; } \ No newline at end of file diff --git a/pmu/pfm/uncore.h b/pmu/pfm/uncore.h index cab7388..3599239 100644 --- a/pmu/pfm/uncore.h +++ b/pmu/pfm/uncore.h @@ -16,8 +16,6 @@ #define UNCORE_H #include "pmu_event.h" -int64_t GetUncoreEventConfig(const char* pmuName); - -int FillUncoreFields(const char* pmuName, PmuEvt *evt); +struct PmuEvt* GetUncoreEvent(const char* pmuName, int collectType); #endif \ No newline at end of file -- Gitee From 1d7d9b5c22a8de102e3a6bc6b504d87d9b6f12ae Mon Sep 17 00:00:00 2001 From: Galaxy Date: Wed, 10 Apr 2024 01:49:28 -0700 Subject: [PATCH 11/14] Implement new interface: PmuEnable, PmuDisable and PmuAppendData PmuEnable and PmuDisable allow users to control sampling flow by themselves. PmuEnable and PmuDisable simply call ioctl to operate buffer fd to start and stop pmu collecting. Therefore, when PmuRead is called, pmu device is still collecting and pmu data may contain collection process data. Spe sampling needs some special processing. When buffer is full and pmu fd is not disabled, nothing can be read from ring buffer. Thus, each time before reading spe buffer, pmu fd needs to be disabled. Disable function is called in PmuRead when task type is SPE_SAMPLING. --- include/pcerrc.h | 23 ++----- include/pmu.h | 25 ++++++++ pmu/evt.cpp | 40 +++++++++--- pmu/evt.h | 14 +++-- pmu/evt_list.cpp | 28 ++++++++- pmu/evt_list.h | 1 + pmu/pfm/core.cpp | 6 +- pmu/pmu.cpp | 17 ++++++ pmu/pmu_list.cpp | 144 ++++++++++++++++++++++++++++++++------------ pmu/pmu_list.h | 4 +- pmu/sampler.cpp | 8 +-- pmu/sampler.h | 1 - pmu/spe.cpp | 70 ++++++++++++++------- pmu/spe.h | 8 +-- pmu/spe_sampler.cpp | 24 +++++--- pmu/spe_sampler.h | 8 ++- 16 files changed, 296 insertions(+), 125 deletions(-) diff --git a/include/pcerrc.h b/include/pcerrc.h index dc0fc6d..bcdf187 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -65,22 +65,11 @@ extern "C" { #define LIBPERF_ERR_NO_PROC 1021 #define LIBPERF_ERR_TOO_MANY_FD 1022 #define LIBPERF_ERR_RAISE_FD 1023 -// libebpf 3000-4000 - -// numafast 4001-5000 -#define NUMAFAST_ERR_SPE_UNAVAILABLE 4001 -#define NUMAFAST_ERR_SET_RES_LIMIT 4002 -#define NUMAFAST_ERR_FILE_OPERATION 4003 -#define NUMAFAST_ERR_INIT_RES 4004 -#define NUMAFAST_ERR_INIT_NODE_COLLECTION 4005 -#define NUMAFAST_ERR_INIT_ANALYZE 4006 -#define NUMAFAST_ERR_INIT_UNCORE_EVENT 4007 -#define NUMAFAST_ERR_ENABLE_UNCORE_EVENT 4008 -#define NUMAFAST_ERR_START_PROFILING 4009 -#define NUMAFAST_ERR_ANALYZE 4010 -#define NUMAFAST_ERR_HELP 4011 -#define NUMAFAST_ERR_BUFFER_INIT 4012 -#define NUMAFAST_ERR_PARAM 4999 +#define LIBPERF_ERR_INVALID_PMU_DATA 1024 +#define LIBPERF_ERR_FAILED_PMU_ENABLE 1025 +#define LIBPERF_ERR_FAILED_PMU_DISABLE 1026 +#define LIBPERF_ERR_FAILED_PMU_RESET 1027 +#define LIBPERF_ERR_NOT_OPENED 1028 #define UNKNOWN_ERROR 9999 @@ -96,4 +85,4 @@ const char* Perror(); #ifdef __cplusplus } #endif -#endif \ No newline at end of file +#endif diff --git a/include/pmu.h b/include/pmu.h index bbf9db2..b7e4ee9 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -113,6 +113,20 @@ struct PmuData { */ int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr); +/** + * @brief + * Enable counting or sampling of task . + * On error, -1 is returned. + */ +int PmuEnable(int pd); + +/** + * @brief + * Disable counting or sampling of task . + * On error, -1 is returned. + */ +int PmuDisable(int pd); + /** * @brief * Collect milliseconds. If is equal to - 1 and the PID list is not empty, the collection @@ -144,6 +158,17 @@ void PmuStop(int pd); */ int PmuRead(int pd, struct PmuData** pmuData); +/** + * @brief + * Append data list to another data list <*toData>. + * The pointer of data list <*toData> will be refreshed after this function is called. + * On success, length of <*toData> is returned. + * On error, -1 is returned. + * @param fromData data list which will be copied to <*toData> + * @param toData pointer to target data list. If data list <*toData> is NULL, a new list will be created. + */ +int PmuAppendData(struct PmuData *fromData, struct PmuData **toData); + /** * @brief Close all the file descriptor opened during collecting process */ diff --git a/pmu/evt.cpp b/pmu/evt.cpp index 8d9f17a..84fdeea 100644 --- a/pmu/evt.cpp +++ b/pmu/evt.cpp @@ -19,6 +19,7 @@ #include #include #include +#include "pcerrc.h" #include "evt.h" enum class HEAD_SIZE { @@ -34,34 +35,53 @@ int KUNPENG_PMU::PerfEventOpen(struct perf_event_attr *attr, pid_t pid, int cpu, return syscall(__NR_perf_event_open, attr, pid, cpu, groupFd, flags); } -bool KUNPENG_PMU::PerfEvt::Enable() +int KUNPENG_PMU::PerfEvt::Enable() { - return (ioctl(this->fd, PERF_EVENT_IOC_ENABLE, 0) == 0); + if (ioctl(this->fd, PERF_EVENT_IOC_ENABLE, 0) == 0) { + return SUCCESS; + } + return LIBPERF_ERR_FAILED_PMU_ENABLE; } -bool KUNPENG_PMU::PerfEvt::Reset() +int KUNPENG_PMU::PerfEvt::Reset() { - return (ioctl(this->fd, PERF_EVENT_IOC_RESET, 0) == 0); + if (ioctl(this->fd, PERF_EVENT_IOC_RESET, 0) == 0) { + return SUCCESS; + } + return LIBPERF_ERR_FAILED_PMU_RESET; } -bool KUNPENG_PMU::PerfEvt::Disable() +int KUNPENG_PMU::PerfEvt::Disable() { - return ioctl(this->fd, PERF_EVENT_IOC_DISABLE, 0); + if (ioctl(this->fd, PERF_EVENT_IOC_DISABLE, 0)) { + return SUCCESS; + } + return LIBPERF_ERR_FAILED_PMU_DISABLE; } -bool KUNPENG_PMU::PerfEvt::Close() +int KUNPENG_PMU::PerfEvt::Close() { close(this->fd); - return true; + return SUCCESS; +} + +int KUNPENG_PMU::PerfEvt::BeginRead() +{ + return SUCCESS; +} + +int KUNPENG_PMU::PerfEvt::EndRead() +{ + return SUCCESS; } -bool KUNPENG_PMU::PerfEvt::Start() +int KUNPENG_PMU::PerfEvt::Start() { this->Reset(); return this->Enable(); } -bool KUNPENG_PMU::PerfEvt::Pause() +int KUNPENG_PMU::PerfEvt::Pause() { return this->Disable(); } diff --git a/pmu/evt.h b/pmu/evt.h index 1275229..6541136 100644 --- a/pmu/evt.h +++ b/pmu/evt.h @@ -33,12 +33,14 @@ public: {} ~PerfEvt() {} - virtual bool Start(); - virtual bool Pause(); - virtual bool Disable(); - virtual bool Enable(); - virtual bool Reset(); - virtual bool Close(); + virtual int Start(); + virtual int Pause(); + virtual int Disable(); + virtual int Enable(); + virtual int Reset(); + virtual int Close(); + virtual int BeginRead(); + virtual int EndRead(); virtual int Init() = 0; diff --git a/pmu/evt_list.cpp b/pmu/evt_list.cpp index 3ef75f1..5302a14 100644 --- a/pmu/evt_list.cpp +++ b/pmu/evt_list.cpp @@ -69,8 +69,9 @@ int KUNPENG_PMU::EvtList::CollectorXYArrayDoTask(int cpuCnt, int pidCnt, std::ve { for (int row = 0; row < cpuCnt; row++) { for (int col = 0; col < pidCnt; col++) { - if (!CollectorDoTask(xyArray[row][col], task)) { - continue; + auto err = CollectorDoTask(xyArray[row][col], task); + if (err != SUCCESS) { + return err; } } } @@ -132,6 +133,11 @@ int KUNPENG_PMU::EvtList::Close() return SUCCESS; } +int KUNPENG_PMU::EvtList::Reset() +{ + return CollectorXYArrayDoTask(this->numCpu, this->numPid, this->xyCounterArray, RESET); +} + void KUNPENG_PMU::EvtList::FillFields( const size_t &start, const size_t &end, CpuTopology *cpuTopo, ProcTopology *procTopo, vector &data) { @@ -147,6 +153,15 @@ void KUNPENG_PMU::EvtList::FillFields( int KUNPENG_PMU::EvtList::Read(vector &data, std::vector &sampleIps) { + for (unsigned int row = 0; row < numCpu; row++) { + for (unsigned int col = 0; col < numPid; col++) { + int err = this->xyCounterArray[row][col]->BeginRead(); + if (err != SUCCESS) { + return err; + } + } + } + struct PmuEvtData* head = nullptr; for (unsigned int row = 0; row < numCpu; row++) { auto cpuTopo = this->cpuList[row].get(); @@ -164,6 +179,15 @@ int KUNPENG_PMU::EvtList::Read(vector &data, std::vector FillFields(cnt, data.size(), cpuTopo, pidList[col].get(), data); } } + + for (unsigned int row = 0; row < numCpu; row++) { + for (unsigned int col = 0; col < numPid; col++) { + int err = this->xyCounterArray[row][col]->EndRead(); + if (err != SUCCESS) { + return err; + } + } + } return SUCCESS; } diff --git a/pmu/evt_list.h b/pmu/evt_list.h index e471764..a431bb7 100644 --- a/pmu/evt_list.h +++ b/pmu/evt_list.h @@ -44,6 +44,7 @@ public: int Start(); int Enable(); int Stop(); + int Reset(); int Read(std::vector &pmuData, std::vector &sampleIps); void SetTimeStamp(const int64_t ×tamp) diff --git a/pmu/pfm/core.cpp b/pmu/pfm/core.cpp index 10980da..22517cc 100644 --- a/pmu/pfm/core.cpp +++ b/pmu/pfm/core.cpp @@ -601,6 +601,6 @@ const std::unordered_map HIP_B_CORE_PMU_MA }; const KUNPENG_PMU::CORE_EVT_MAP KUNPENG_PMU::CORE_EVENT_MAP = { - {CHIP_TYPE::KUNPENG_HIP_A, HIP_A_CORE_PMU_MAP}, - {CHIP_TYPE::KUNPENG_HIP_B, HIP_B_CORE_PMU_MAP}, -}; \ No newline at end of file + {CHIP_TYPE::HIPA, HIP_A_CORE_PMU_MAP}, + {CHIP_TYPE::HIPB, HIP_B_CORE_PMU_MAP}, +}; diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index 9859a20..9dfcefd 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -172,6 +172,23 @@ int PmuOpen(enum PmuTaskType collectType, struct PmuAttr *attr) } } +int PmuEnable(int pd) +{ + return PmuCollectStart(pd); +} + +int PmuDisable(int pd) +{ + return PmuCollectPause(pd); +} + +int PmuAppendData(struct PmuData *fromData, struct PmuData **toData) +{ + int toLen = 0; + PmuList::GetInstance()->AppendData(fromData, toData, toLen); + return toLen; +} + static int DoCollectCounting(int pd, int milliseconds) { constexpr int collectInterval = 100; diff --git a/pmu/pmu_list.cpp b/pmu/pmu_list.cpp index b7e5c54..691ecfb 100644 --- a/pmu/pmu_list.cpp +++ b/pmu/pmu_list.cpp @@ -33,23 +33,17 @@ namespace KUNPENG_PMU { std::mutex PmuList::pmuListMtx; std::mutex PmuList::dataListMtx; - int PmuList::CheckRlimit(const std::vector& cpuTopoList, const std::vector procTopoList, - const PmuTaskAttr* head) + int PmuList::CheckRlimit(const unsigned fdNum) { - unsigned long length = 0; - const PmuTaskAttr* current = head; - while (current != nullptr) { - ++length; - current = current->next; - } - unsigned long need = length * cpuTopoList.size() * procTopoList.size(); - return RaiseNumFd(need); + return RaiseNumFd(fdNum); } int PmuList::Register(const int pd, PmuTaskAttr* taskParam) { - SymResolverInit(); - SymResolverRecordKernel(); + if (taskParam->pmuEvt->collectType != COUNTING) { + SymResolverInit(); + SymResolverRecordKernel(); + } /* Use libpfm to get the basic config for this pmu event */ struct PmuTaskAttr* pmuTaskAttrHead = taskParam; // Init collect type for pmu data, @@ -59,31 +53,35 @@ namespace KUNPENG_PMU { evtData.collectType = static_cast(pmuTaskAttrHead->pmuEvt->collectType); evtData.pd = pd; } - /** - * Create cpu topology list - */ - std::vector cpuTopoList; - auto err = PrepareCpuTopoList(pd, pmuTaskAttrHead, cpuTopoList); - if (err != SUCCESS) { - return err; - } - - /** - * Create process topology list - */ - std::vector procTopoList; - err = PrepareProcTopoList(pmuTaskAttrHead, procTopoList); - if (err != SUCCESS) { - return err; - } - err = CheckRlimit(cpuTopoList, procTopoList, pmuTaskAttrHead); - if (err != SUCCESS) { - return err; - } + + unsigned fdNum = 0; while (pmuTaskAttrHead) { + /** + * Create cpu topology list + */ + std::vector cpuTopoList; + auto err = PrepareCpuTopoList(pd, pmuTaskAttrHead, cpuTopoList); + if (err != SUCCESS) { + return err; + } + + /** + * Create process topology list + */ + std::vector procTopoList; + err = PrepareProcTopoList(pmuTaskAttrHead, procTopoList); + if (err != SUCCESS) { + return err; + } + fdNum += cpuTopoList.size(); + procTopoList.size(); std::shared_ptr evtList = std::make_shared(cpuTopoList, procTopoList, pmuTaskAttrHead->pmuEvt); - err = evtList->Init(); + InsertEvtList(pd, evtList); + pmuTaskAttrHead = pmuTaskAttrHead->next; + } + + for (auto evtList : GetEvtList(pd)) { + auto err = evtList->Init(); if (err != SUCCESS) { return err; } @@ -91,8 +89,11 @@ namespace KUNPENG_PMU { if (err != SUCCESS) { return err; } - InsertEvtList(pd, evtList); - pmuTaskAttrHead = pmuTaskAttrHead->next; + } + + auto err = CheckRlimit(fdNum); + if (err != SUCCESS) { + return err; } return SUCCESS; } @@ -101,7 +102,10 @@ namespace KUNPENG_PMU { { auto pmuList = GetEvtList(pd); for (auto item : pmuList) { - item->Start(); + auto err = item->Start(); + if (err != SUCCESS) { + return err; + } } return SUCCESS; } @@ -110,7 +114,10 @@ namespace KUNPENG_PMU { { auto pmuList = GetEvtList(pd); for (auto item : pmuList) { - item->Pause(); + auto err = item->Pause(); + if (err != SUCCESS) { + return err; + } } return SUCCESS; } @@ -119,7 +126,25 @@ namespace KUNPENG_PMU { { // Exchange data in to . // Return a pointer to data. + + auto &evtData = GetDataList(pd); + if (evtData.data.empty()) { + // Have not read ring buffer yet. + // Mostly caller is using PmuEnable and PmuDisable mode. + auto err = ReadDataToBuffer(pd); + if (err != SUCCESS) { + return userDataList[nullptr].data; + } + } auto& userData = ExchangeToUserData(pd); + if (GetTaskType(pd) == COUNTING) { + // Reset counter after Read is called, + // thus count in PmuData is pmu count in last epoch. + auto evtList = GetEvtList(pd); + for (auto item : evtList) { + item->Reset(); + } + } return userData; } @@ -141,6 +166,49 @@ namespace KUNPENG_PMU { return SUCCESS; } + int PmuList::AppendData(PmuData *fromData, PmuData **toData, int &len) + { + if (toData == nullptr || fromData == nullptr) { + return LIBPERF_ERR_INVALID_PMU_DATA; + } + + lock_guard lg(dataListMtx); + auto findFromData = userDataList.find(fromData); + if (findFromData == userDataList.end()) { + return LIBPERF_ERR_INVALID_PMU_DATA; + } + + if (*toData == nullptr) { + // For null target data list, copy source list to target. + // A new pointer to data list is created and is assigned to <*toData>. + 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 (findFromData == userDataList.end()) { + return LIBPERF_ERR_INVALID_PMU_DATA; + } + // For non-null target data list, append source list to end of target vector. + auto &dataVec = findToData->second.data; + dataVec.insert(dataVec.end(), findFromData->second.data.begin(), findFromData->second.data.end()); + len = dataVec.size(); + + if (*toData != dataVec.data()) { + // As target vector grows, pointer to list may change. + // Update the pointer and assign to <*toData>. + auto newDataPtr = dataVec.data(); + userDataList[newDataPtr] = move(findToData->second); + userDataList.erase(*toData); + *toData = newDataPtr; + } + return SUCCESS; + } + void PmuList::Close(const int pd) { auto evtList = GetEvtList(pd); diff --git a/pmu/pmu_list.h b/pmu/pmu_list.h index d249769..67b8bd2 100644 --- a/pmu/pmu_list.h +++ b/pmu/pmu_list.h @@ -54,6 +54,7 @@ public: * @return std::vector& */ std::vector& Read(const int pd); + int AppendData(PmuData *fromData, PmuData **toData, int &len); int Start(const int pd); int Pause(const int pd); void Close(const int pd); @@ -105,8 +106,7 @@ private: int PrepareCpuTopoList( const unsigned& pd, PmuTaskAttr* pmuTaskAttrHead, std::vector& cpuTopoList); int PrepareProcTopoList(PmuTaskAttr* pmuTaskAttrHead, std::vector& procTopoList) const; - int CheckRlimit(const std::vector& cpuTopoList, const std::vector procTopoList, - const PmuTaskAttr* head); + int CheckRlimit(const unsigned fdNum); static void AggregateData(const std::vector& evData, std::vector& newEvData); std::vector& GetPreviousData(const unsigned pd); diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index b85fed7..82f80ee 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -34,9 +34,8 @@ using namespace std; -static constexpr int PAGE_SIZE = 1024; +static constexpr int PAGE_SIZE = 4096; static constexpr int MAX_ATTR_SIZE = 120; -static constexpr int SAMPLE_FREQ = 1000; int KUNPENG_PMU::PerfSampler::pages = 128; template @@ -80,11 +79,6 @@ union KUNPENG_PMU::PerfEvent *KUNPENG_PMU::PerfSampler::SampleReadEvent() return ReadEvent(*this->sampleMmap); } -bool KUNPENG_PMU::PerfSampler::Close() -{ - return PerfEvt::Close(); -} - int KUNPENG_PMU::PerfSampler::Mmap() { int mmapLen = (this->pages + 1) * PAGE_SIZE; diff --git a/pmu/sampler.h b/pmu/sampler.h index a17ac02..e9ab432 100644 --- a/pmu/sampler.h +++ b/pmu/sampler.h @@ -41,7 +41,6 @@ namespace KUNPENG_PMU { int Init() override; int Read(std::vector &data, std::vector &sampleIps) override; - bool Close() override; int MapPerfAttr() override; diff --git a/pmu/spe.cpp b/pmu/spe.cpp index 1b27da6..f76dc3b 100644 --- a/pmu/spe.cpp +++ b/pmu/spe.cpp @@ -239,41 +239,59 @@ int SpeOpen(PmuEvt *attr, int cpu, SpeContext *ctx) static int CoreSpeEnable(struct SpeCoreContext *ctx) { if (ctx->dummyFd <= 0 || ctx->speFd <= 0) { - return -1; + return LIBPERF_ERR_FAILED_PMU_ENABLE; } - ioctl(ctx->dummyFd, PERF_EVENT_IOC_ENABLE, 0); - ioctl(ctx->speFd, PERF_EVENT_IOC_ENABLE, 0); - return 0; + auto err = ioctl(ctx->dummyFd, PERF_EVENT_IOC_ENABLE, 0); + if (err != SUCCESS) { + return LIBPERF_ERR_FAILED_PMU_ENABLE; + } + err = ioctl(ctx->speFd, PERF_EVENT_IOC_ENABLE, 0); + if (err != SUCCESS) { + return LIBPERF_ERR_FAILED_PMU_ENABLE; + } + return SUCCESS; } int SpeEnable(struct SpeContext *ctx) { for (int i = 0; i < ctx->cpuNum; i++) { - CoreSpeEnable(&ctx->coreCtxes[i]); + auto err = CoreSpeEnable(&ctx->coreCtxes[i]); + if (err != SUCCESS) { + return err; + } } - return 0; + return SUCCESS; } static int CoreSpeDisable(struct SpeCoreContext *ctx) { if (ctx->dummyFd <= 0 || ctx->speFd <= 0) { - return -1; + return LIBPERF_ERR_FAILED_PMU_DISABLE; } - ioctl(ctx->speFd, PERF_EVENT_IOC_DISABLE, 0); - ioctl(ctx->dummyFd, PERF_EVENT_IOC_DISABLE, 0); - return 0; + auto err = ioctl(ctx->speFd, PERF_EVENT_IOC_DISABLE, 0); + if (err != SUCCESS) { + return LIBPERF_ERR_FAILED_PMU_DISABLE; + } + err = ioctl(ctx->dummyFd, PERF_EVENT_IOC_DISABLE, 0); + if (err != SUCCESS) { + return LIBPERF_ERR_FAILED_PMU_DISABLE; + } + return SUCCESS; } -void SpeDisable(struct SpeContext *ctx) +int SpeDisable(struct SpeContext *ctx) { for (int i = 0; i < ctx->cpuNum; i++) { - CoreSpeDisable(&ctx->coreCtxes[i]); + auto err = CoreSpeDisable(&ctx->coreCtxes[i]); + if (err != SUCCESS) { + return err; + } } - return; + return SUCCESS; } void SpeClose(struct SpeContext *ctx) @@ -523,36 +541,42 @@ int Spe::Open(PmuEvt *attr) return SUCCESS; } -bool Spe::Enable(bool clearPrevRecords) +int Spe::Enable(bool clearPrevRecords) { if (clearPrevRecords) { pidRecords.clear(); } if (!(status & OPENED)) { - return false; + return LIBPERF_ERR_FAILED_PMU_ENABLE; } if (status & ENABLED) { - return true; + return SUCCESS; + } + auto err = SpeEnable(ctx); + if (err != SUCCESS) { + return err; } - SpeEnable(ctx); status &= ~DISABLED; status &= ~READ; status |= ENABLED; - return true; + return SUCCESS; } -bool Spe::Disable() +int Spe::Disable() { if (!(status & OPENED)) { - return false; + return LIBPERF_ERR_FAILED_PMU_DISABLE; } if (status & DISABLED) { - return true; + return SUCCESS; + } + auto err = SpeDisable(ctx); + if (err != SUCCESS) { + return err; } - SpeDisable(ctx); status &= ~ENABLED; status |= DISABLED; - return true; + return SUCCESS; } bool Spe::Close() { diff --git a/pmu/spe.h b/pmu/spe.h index dc1f5c5..7e5697f 100644 --- a/pmu/spe.h +++ b/pmu/spe.h @@ -151,17 +151,13 @@ public: /** * @brief Start collect. * @param clearPrevRecords whether clear all records from previos collection. - * @return true - * @return false */ - bool Enable(bool clearPrevRecords = true); + int Enable(bool clearPrevRecords = true); /** * @brief Stop collect. - * @return true - * @return false */ - bool Disable(); + int Disable(); /** * @brief Free ring buffer. diff --git a/pmu/spe_sampler.cpp b/pmu/spe_sampler.cpp index 7b6a39d..9cca5e4 100644 --- a/pmu/spe_sampler.cpp +++ b/pmu/spe_sampler.cpp @@ -148,31 +148,41 @@ namespace KUNPENG_PMU { } } - bool PerfSpe::Enable() + int PerfSpe::BeginRead() + { + return Disable(); + } + + int PerfSpe::EndRead() + { + return Enable(); + } + + int PerfSpe::Enable() { auto findSpe = speSet.find(this->cpu); if (findSpe == speSet.end()) { - return false; + return LIBPERF_ERR_NOT_OPENED; } return findSpe->second.Enable(); } - bool PerfSpe::Disable() + int PerfSpe::Disable() { auto findSpe = speSet.find(this->cpu); if (findSpe == speSet.end()) { - return false; + return LIBPERF_ERR_NOT_OPENED; } return findSpe->second.Disable(); } - bool PerfSpe::Close() + int PerfSpe::Close() { auto findSpe = speSet.find(this->cpu); if (findSpe == speSet.end()) { - return true; + return SUCCESS; } findSpe->second.Close(); @@ -181,7 +191,7 @@ namespace KUNPENG_PMU { delete[] extPtr; } extPool.clear(); - return true; + return SUCCESS; } diff --git a/pmu/spe_sampler.h b/pmu/spe_sampler.h index 2a45463..2f503ce 100644 --- a/pmu/spe_sampler.h +++ b/pmu/spe_sampler.h @@ -41,10 +41,12 @@ namespace KUNPENG_PMU { int MapPerfAttr() override; bool Mmap(); - bool Disable() override; - bool Enable() override; - bool Close() override; + int Disable() override; + int Enable() override; + int Close() override; + int BeginRead() override; + int EndRead() override; private: bool SpeExist(int cpu) const; void InsertSpeRecords(const int &tid, const std::vector &speRecords, std::vector &data, -- Gitee From 37688b84f6add5a448f86e541f58d8d94a5ab958 Mon Sep 17 00:00:00 2001 From: DCHii <13780064348@163.com> Date: Thu, 11 Apr 2024 19:51:05 +0800 Subject: [PATCH 12/14] Place dynamic libraries and header files --- .gitmodules | 3 ++ CMakeLists.txt | 17 +------- Common.cmake | 43 ++----------------- build.sh | 93 ++++++---------------------------------- include/pmu.h | 4 +- pmu/CMakeLists.txt | 37 +++------------- pmu/pfm/core.cpp | 2 +- pmu/pmu.cpp | 2 + pmu/pmu_event.h | 2 + pmu/sampler.cpp | 3 ++ symbol/CMakeLists.txt | 2 + third_party/elfin-parser | 1 + 12 files changed, 41 insertions(+), 168 deletions(-) create mode 100644 .gitmodules create mode 160000 third_party/elfin-parser diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..fa50f14 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "elfin-parser"] + path = third_party/elfin-parser + url = https://gitee.com/openeuler/elfin-parser.git \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e98a59..52c7be2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,38 +16,23 @@ if (POLICY CMP0048) endif (POLICY CMP0048) set(PROJECT_TOP_DIR ${CMAKE_CURRENT_LIST_DIR}) project(libkprof) +set(CMAKE_INSTALL_PREFIX "${PROJECT_TOP_DIR}/output/" CACHE PATH "Installation directory" FORCE) cmake_minimum_required (VERSION 3.12.0) - set(CMAKE_CXX_STANDARD 11) -if (INCLUDE_TEST) - set(CMAKE_CXX_STANDARD 14) -endif() - set(CMAKE_C_STANDARD 11) -set(CMAKE_INSTALL_PREFIX "${PROJECT_TOP_DIR}/output/test" CACHE PATH "Installation directory" FORCE) - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.8.5) message(FATAL_ERROR "GCC 4.8.5 or newer required") endif() - if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Release) endif() -if (BUILD_TESTS) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} --coverage") -endif() - set(TOP_DIR ${PROJECT_SOURCE_DIR}) message("TOP_DIR is ${TOP_DIR}") include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) -# add_subdirectory(util) add_subdirectory(symbol) add_subdirectory(pmu) -if (INCLUDE_TEST) - add_subdirectory(test) -endif() set(CMAKE_EXPORT_COMPILE_COMMANDS True) \ No newline at end of file diff --git a/Common.cmake b/Common.cmake index 7a2d22a..97019cc 100644 --- a/Common.cmake +++ b/Common.cmake @@ -12,7 +12,6 @@ # Description: Set general configuration information. - set(CMAKE_CXX_FLAGS_DEBUG " -O0 -g -Wall -pg -fno-gnu-unique -DDEBUG") set(CMAKE_CXX_FLAGS_RELEASE " -O3 -DNDEBUG -fstack-protector-all -Wl,-z,relro,-z,now -Wall -fPIE -s -fno-gnu-unique") set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "-O3 -g") @@ -23,47 +22,13 @@ set(CMAKE_C_FLAGS_RELWITHDEBINFO "-O3 -g") set(CMAKE_C_FLAGS_MINSIZEREL "-Os -DNDEBUG") set(CMAKE_EXE_LINKER_FLAGS_RELEASE " -Wl,-z,relro,-z,now,-z,noexecstack -pie -s") -set(THIRD_PARTY ${PROJECT_TOP_DIR}/../opensource) +set(THIRD_PARTY ${PROJECT_TOP_DIR}/third_party) add_compile_options(-w) # 调试阶段先去除告警 -if(NOT DEFINED OPEN_SOURCE_DIR) - set(OPEN_SOURCE_DIR "${PROJECT_SOURCE_DIR}/../opensource") -endif() - -# 添加一个library -#GIT_REPOSITORY ssh://git@codehub-dg-y.huawei.com:2222/hwsecurec_group/huawei_secure_c.git -#GIT_TAG tag_Huawei_Secure_C_V100R001C01SPC012B002_00001 - -# GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/www.sqlite.org/sqlite.git -# GIT_TAG 3.40.1 - add_library(elf_static STATIC IMPORTED) -set_property(TARGET elf_static PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/elfin-parser/libelf++.a) +set_property(TARGET elf_static PROPERTY IMPORTED_LOCATION ${THIRD_PART}/local/libelf++.a) -add_library(dwarf_static STATIC IMPORTED - include/pcerrc.h - util/pcerr.h - symbol/name_resolve.cpp - symbol/name_resolve.h - symbol/symbol.cpp - symbol/symbol.h - symbol/symbol_resolve.cpp) -set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/elfin-parser/libdwarf++.a) +add_library(dwarf_static STATIC IMPORTED) +set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${THIRD_PART}/local/libdwarf++.a) include_directories(${THIRD_PARTY}/elfin-parser/dwarf) include_directories(${THIRD_PARTY}/elfin-parser/elf) - -#GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/nlohmann/json.git -set(nlohmann_json_SOURCE_DIR ${OPEN_SOURCE_DIR}/json) -message("nlohmann_json_SOURCE_DIR:${nlohmann_json_SOURCE_DIR}") - -#GIT_REPOSITORY ssh://git@szv-open.codehub.huawei.com:2222/OpenSourceCenter/google/googletest.git -#GIT_TAG release-1.12.1 # -include_directories("${OPEN_SOURCE_DIR}/local/googletest/include") -add_library(gtest_main STATIC IMPORTED) -set_property(TARGET gtest_main PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgtest_main.a) -add_library(gtest STATIC IMPORTED) -set_property(TARGET gtest PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgtest.a) -add_library(gmock_main STATIC IMPORTED) -set_property(TARGET gmock_main PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock_main.a) -add_library(gmock STATIC IMPORTED) -set_property(TARGET gmock PROPERTY IMPORTED_LOCATION ${OPEN_SOURCE_DIR}/local/googletest/lib64/libgmock.a) diff --git a/build.sh b/build.sh index 3a462c5..cf47fd6 100644 --- a/build.sh +++ b/build.sh @@ -14,46 +14,27 @@ set -e set -x -if [ -d ${WORKSPACE}/code ]; then - PROJECT_DIR=${WORKSPACE}/code/libprof -else - CURRENT_DIR=$(cd $(dirname "$0"); pwd) - PROJECT_DIR=$(realpath "${CURRENT_DIR}") -fi -if [[ -d "${PROJECT_DIR}/../opensource" ]] ;then - OPENSOURCE_DIR=$(realpath "${PROJECT_DIR}/../opensource") -else - OPENSOURCE_DIR=${PROJECT_DIR}/opensource -fi -PACKAGE_NAME="DevKit-LibProf-24.0.RC1-Linux-Kunpeng" -PACKAGE_PATH="${PROJECT_DIR}/output/${PACKAGE_NAME}" +CURRENT_DIR=$(cd $(dirname "$0"); pwd) +PROJECT_DIR=$(realpath "${CURRENT_DIR}") + BUILD_DIR=${PROJECT_DIR}/_build +THIRD_PARTY=${PROJECT_DIR}/third_party/ source ${PROJECT_DIR}/build/common.sh creat_dir "${BUILD_DIR}" -creat_dir "${PACKAGE_PATH}" # 指定所有依赖使用的gcc export CC=gcc export CXX=g++ -if [ -d "${OPENSOURCE_DIR}/local" ];then - echo ${OPENSOURCE_DIR} "is exist" +if [ -d "${THIRD_PARTY}/local" ];then + echo ${THIRD_PARTY} "is exist" else - echo ${OPENSOURCE_DIR} "is not exist" - mkdir ${OPENSOURCE_DIR}/local -fi - -# 默认情况下不包含测试目录 -INCLUDE_TEST=OFF - -# 如果第一个参数是 "test",则设置 INCLUDE_TEST 为 ON -if [[ "$1" == "test" ]]; then - build_googletest $OPENSOURCE_DIR - INCLUDE_TEST=ON + echo ${THIRD_PARTY} "is not exist" + mkdir ${THIRD_PARTY}/local fi # build libprof.so libraries including libprocfs.so libprocfs.a libpmu.so libpmu.a libtrace.so libtrace.so function build_elfin() { - local cmake_target_dir=$OPENSOURCE_DIR/local/elfin-parser + local cmake_target_dir=$THIRD_PARTY/local/elfin-parser rm -rf ${cmake_target_dir} if [ -d "${cmake_target_dir}" ];then echo ${cmake_target_dir} "is exist" @@ -62,7 +43,7 @@ function build_elfin() { echo ${cmake_target_dir} "is not exist" mkdir ${cmake_target_dir} fi - cd "$OPENSOURCE_DIR/elfin-parser" + cd "$THIRD_PARTY/elfin-parser" rm -rf build sed -i 's/-mcpu=tsv110//g' Common.cmake sed -i 's/-mno-outline-atomics//g' Common.cmake @@ -74,72 +55,22 @@ function build_elfin() { cd build cmake -DCMAKE_INSTALL_PREFIX=${cmake_target_dir} -DCMAKE_CXX_FLAGS="-fPIC" .. make --silent -j 64 -# mkdir -p ${cmake_target_dir} cp ./lib64/libdwarf++.a ./lib64/libelf++.a ${cmake_target_dir} -# popd - echo "install log path: $cmake_target_dir" - cd ../../../libprof -} - -function build_huawei_securec(){ - if [ -d "${OPENSOURCE_DIR}/local" ];then - echo ${OPENSOURCE_DIR} "is exist" - else - echo ${OPENSOURCE_DIR} "is not exist" - mkdir ${OPENSOURCE_DIR}/local - fi - local cmake_target_dir=$OPENSOURCE_DIR/local/huawei_secure_c - if [ -d "${cmake_target_dir}" ];then - echo ${cmake_target_dir} "is exist" - return - else - echo ${cmake_target_dir} "is not exist" - fi - pushd "$OPENSOURCE_DIR/huawei_secure_c/src" - sed -i 's/-Wdate-time//g' Makefile - sed -i 's/-Wduplicated-branches//g' Makefile - sed -i 's/-Wduplicated-cond//g' Makefile - sed -i 's/-Wimplicit-fallthrough=3//g' Makefile - sed -i 's/-Wshift-negative-value//g' Makefile - sed -i 's/-Wshift-overflow=2//g' Makefile - make lib - mkdir -p $cmake_target_dir - cp -rf "$OPENSOURCE_DIR/huawei_secure_c/lib" $cmake_target_dir - cp -rf "$OPENSOURCE_DIR/huawei_secure_c/include" $cmake_target_dir - popd echo "install log path: $cmake_target_dir" } build_libprof() { cd $BUILD_DIR - cmake -DINCLUDE_TEST=$INCLUDE_TEST .. + cmake .. make -j ${cpu_core_num} + make install echo "build_libprof success" } -build_package() -{ - cd $PROJECT_DIR/output - cp -rf ${PROJECT_DIR}/include ${PACKAGE_PATH} - cp -rf ${PROJECT_DIR}/symbol/symbol.h ${PACKAGE_PATH}/include/ - cp -rf ${BUILD_DIR}/pmu/libperf.so ${PACKAGE_PATH} - cp -rf ${BUILD_DIR}/symbol/libsym.so ${PACKAGE_PATH} - tar -czf "${PACKAGE_NAME}.tar.gz" "${PACKAGE_NAME}" --owner=root --group=root - if [ "${IS_BUILDCHECK}" != "true" -a -d "${WORKSPACE}/output_${ENV_PIPELINE_JOB_ID}/libprof" ];then - cp -rf ${PACKAGE_NAME}.tar.gz ${WORKSPACE}/output_${ENV_PIPELINE_JOB_ID}/libprof - fi -} - main() { - cd $PROJECT_DIR/ && python "build/generate_config.py" build_elfin - build_huawei_securec - build_sqlite3 $OPENSOURCE_DIR build_libprof - build_package -# bash ${CURRENT_DIR}/test/test.sh ${PROJECT_DIR}/output/test ${PACKAGE_PATH} } -# bash build.sh test来获取UT main $@ \ No newline at end of file diff --git a/include/pmu.h b/include/pmu.h index b7e4ee9..f8a79ba 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -69,6 +69,8 @@ struct PmuAttr { unsigned freq; // sample frequency }; unsigned useFreq : 1; + unsigned excludeUser : 1; // don't count user + unsigned excludeKernel : 1; // don't count kernel // SPE related fields. enum SpeFilter dataFilter; // spe data filter @@ -91,7 +93,7 @@ struct PmuData { unsigned cpu; // cpu id struct CpuTopology *cpuTopo; // cpu topology const char *comm; // process command - + int period; // number of Samples union { uint64_t count; // event count. Only available for Counting. struct PmuDataExt *ext; // extension. Only available for Spe. diff --git a/pmu/CMakeLists.txt b/pmu/CMakeLists.txt index d7a124d..d344009 100644 --- a/pmu/CMakeLists.txt +++ b/pmu/CMakeLists.txt @@ -10,52 +10,29 @@ cmake_minimum_required (VERSION 3.12.0) # Related directory settings # set(UTIL_FILE_DIR ${PROJECT_TOP_DIR}/util) set(SYMBOL_FILE_DIR ${PROJECT_TOP_DIR}/symbol) -set(SKELETON_FILE_DIR ${PROJECT_TOP_DIR}/skeleton) set(PMU_FILE_DIR ${PROJECT_TOP_DIR}/pmu) set(PFM_FILE_DIR ${PROJECT_TOP_DIR}/pmu/pfm) set(PMU_DECODER_DIR ${PMU_FILE_DIR}/decoder) # Source files # file(GLOB UTIL_SRC ${UTIL_FILE_DIR}/*.cpp) - -file(GLOB PMU_SRC ${PMU_FILE_DIR}/*c ${PMU_FILE_DIR}/*cpp ${PMU_FILE_DIR}/analyzer/*cpp - ${PMU_FILE_DIR}/analyzer/metric/*cpp ${PMU_FILE_DIR}/analyzer/numafast/*c - ${PMU_FILE_DIR}/analyzer/numafast/util/*c ${PMU_FILE_DIR}/analyzer/numafast/analyze/*c) +file(GLOB PMU_SRC ${PMU_FILE_DIR}/*c ${PMU_FILE_DIR}/*cpp) file(GLOB PMU_DECODER_SRC ${PMU_DECODER_DIR}/*.cpp) file(GLOB SYMBOL_SRC ${SYMBOL_FILE_DIR}/*c ${SYMBOL_FILE_DIR}/*cpp) file(GLOB PFM_SRC ${PFM_FILE_DIR}/*c ${PFM_FILE_DIR}/*cpp) -set(PFM_FILE_DIR ${PROJECT_TOP_DIR}/pmu/pfm) -file(GLOB PFM_SRC ${PFM_FILE_DIR}/*c ${PFM_FILE_DIR}/*cpp) - include_directories(${PROJECT_TOP_DIR}/include) include_directories(${PMU_FILE_DIR}/) -include_directories(${PROJECT_TOP_DIR}/include) include_directories(${PFM_FILE_DIR}) -include_directories(${PROJECT_TOP_DIR}/include) # directories for ultilities and symbol resolving include_directories(${UTIL_FILE_DIR}) include_directories(${SYMBOL_FILE_DIR}) -include_directories(${PMU_FILE_DIR}/analyzer) -include_directories(${PMU_FILE_DIR}/analyzer/metric) -include_directories(${PMU_FILE_DIR}/analyzer/numafast) -include_directories(${PMU_FILE_DIR}/analyzer/numafast/util) -include_directories(${PMU_FILE_DIR}/analyzer/numafast/analyze) include_directories(${PMU_DECODER_DIR}) -# include skeleton files -include_directories(${SKELETON_FILE_DIR}) - -# include all the procfs related header files -include_directories(${PROCFS_FILE_DIR}) -include_directories(${PROCFS_FILE_DIR}/analyze) -include_directories(${PROCFS_FILE_DIR}/parser) - -#include thirdparty -include_directories(${THIRD_PARTY}/json/include) - -set(CMAKE_SKIP_RPATH YES) -ADD_LIBRARY(perf SHARED ${PMU_SRC} ${UTIL_SRC} ${PFM_SRC} ${PMU_DECODER_SRC}) -target_link_libraries(perf numa sym) -target_compile_options(perf PRIVATE -fPIC) +ADD_LIBRARY(kperf SHARED ${PMU_SRC} ${UTIL_SRC} ${PFM_SRC} ${PMU_DECODER_SRC}) +target_link_libraries(kperf numa sym) +target_compile_options(kperf PRIVATE -fPIC) +install(TARGETS kperf DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +file(GLOB HEADER_FILES ${PROJECT_TOP_DIR}/include/*.h) +install(FILES ${HEADER_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/pmu/pfm/core.cpp b/pmu/pfm/core.cpp index 22517cc..aa7dd54 100644 --- a/pmu/pfm/core.cpp +++ b/pmu/pfm/core.cpp @@ -603,4 +603,4 @@ const std::unordered_map HIP_B_CORE_PMU_MA const KUNPENG_PMU::CORE_EVT_MAP KUNPENG_PMU::CORE_EVENT_MAP = { {CHIP_TYPE::HIPA, HIP_A_CORE_PMU_MAP}, {CHIP_TYPE::HIPB, HIP_B_CORE_PMU_MAP}, -}; +}; \ No newline at end of file diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index 9dfcefd..a88470f 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -517,6 +517,8 @@ static struct PmuTaskAttr* AssignTaskParam(PmuTaskType collectType, PmuAttr *att taskParam->pmuEvt = shared_ptr(pmuEvt, PmuEvtFree); taskParam->pmuEvt->useFreq = attr->useFreq; taskParam->pmuEvt->period = attr->period; + taskParam->pmuEvt->excludeKernel = attr->excludeKernel; + taskParam->pmuEvt->excludeUser = attr->excludeUser; return taskParam.release(); } diff --git a/pmu/pmu_event.h b/pmu/pmu_event.h index b5e3bd9..992fa3a 100644 --- a/pmu/pmu_event.h +++ b/pmu/pmu_event.h @@ -37,6 +37,8 @@ struct PmuEvt { int collectType; std::string name; // string name of this pmu event int cpumask; // a representative CPU number for each socket (package) in the motherboard. + unsigned excludeUser : 1; // don't count user + unsigned excludeKernel : 1; // don't count kernel union { unsigned period; // sample period unsigned freq; // sample frequency diff --git a/pmu/sampler.cpp b/pmu/sampler.cpp index 82f80ee..bbaa991 100644 --- a/pmu/sampler.cpp +++ b/pmu/sampler.cpp @@ -56,6 +56,8 @@ int KUNPENG_PMU::PerfSampler::MapPerfAttr() attr.freq = this->evt->useFreq; attr.sample_period = this->evt->period; attr.read_format = PERF_FORMAT_ID; + attr.exclude_kernel = this->evt->excludeKernel; + attr.exclude_user = this->evt->excludeUser; attr.pinned = 1; attr.disabled = 1; attr.inherit = 1; @@ -134,6 +136,7 @@ void KUNPENG_PMU::PerfSampler::RawSampleProcess( current->cpu = static_cast(sample->cpu); current->pid = static_cast(sample->pid); current->tid = static_cast(sample->tid); + current->period = static_cast(sample->period); } void KUNPENG_PMU::PerfSampler::ReadRingBuffer(vector &data, vector &sampleIps) diff --git a/symbol/CMakeLists.txt b/symbol/CMakeLists.txt index 2e97aac..e17614b 100644 --- a/symbol/CMakeLists.txt +++ b/symbol/CMakeLists.txt @@ -17,3 +17,5 @@ include_directories(${INCLUDE_DIR}) message(${THIRD_PARTY}/elfin-parser/elf) ADD_LIBRARY(sym SHARED ${SYMBOL_SRC}) target_link_libraries(sym elf_static dwarf_static pthread) +install(TARGETS sym DESTINATION ${CMAKE_INSTALL_PREFIX}/bin) +install(FILES ${SYMBOL_FILE_DIR}/symbol.h DESTINATION ${CMAKE_INSTALL_PREFIX}/include) diff --git a/third_party/elfin-parser b/third_party/elfin-parser new file mode 160000 index 0000000..13e57e2 --- /dev/null +++ b/third_party/elfin-parser @@ -0,0 +1 @@ +Subproject commit 13e57e29400a3a7bb5cb27c364b4b73468b4050f -- Gitee From aca60e3e300894bc2654e6854fb271dac6f6e99d Mon Sep 17 00:00:00 2001 From: DCHii <13780064348@163.com> Date: Thu, 11 Apr 2024 20:04:40 +0800 Subject: [PATCH 13/14] Build modifications --- Common.cmake | 4 ++-- build.sh | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Common.cmake b/Common.cmake index 97019cc..030dac1 100644 --- a/Common.cmake +++ b/Common.cmake @@ -26,9 +26,9 @@ set(THIRD_PARTY ${PROJECT_TOP_DIR}/third_party) add_compile_options(-w) # 调试阶段先去除告警 add_library(elf_static STATIC IMPORTED) -set_property(TARGET elf_static PROPERTY IMPORTED_LOCATION ${THIRD_PART}/local/libelf++.a) +set_property(TARGET elf_static PROPERTY IMPORTED_LOCATION ${THIRD_PARTY}/local/elfin-parser/libelf++.a) add_library(dwarf_static STATIC IMPORTED) -set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${THIRD_PART}/local/libdwarf++.a) +set_property(TARGET dwarf_static PROPERTY IMPORTED_LOCATION ${THIRD_PARTY}/local/elfin-parser/libdwarf++.a) include_directories(${THIRD_PARTY}/elfin-parser/dwarf) include_directories(${THIRD_PARTY}/elfin-parser/elf) diff --git a/build.sh b/build.sh index cf47fd6..0d56a7d 100644 --- a/build.sh +++ b/build.sh @@ -26,10 +26,10 @@ creat_dir "${BUILD_DIR}" export CC=gcc export CXX=g++ if [ -d "${THIRD_PARTY}/local" ];then - echo ${THIRD_PARTY} "is exist" - else - echo ${THIRD_PARTY} "is not exist" - mkdir ${THIRD_PARTY}/local + echo ${THIRD_PARTY}/local "is exist" +else + echo ${THIRD_PARTY}local "is not exist" + creat_dir ${THIRD_PARTY}/local fi # build libprof.so libraries including libprocfs.so libprocfs.a libpmu.so libpmu.a libtrace.so libtrace.so -- Gitee From 7d2a35fe9d468cd2e2740969cc29e5bb7785ac6a Mon Sep 17 00:00:00 2001 From: DCHii <13780064348@163.com> Date: Thu, 11 Apr 2024 20:36:50 +0800 Subject: [PATCH 14/14] Distinguishing between kernel state and user state during spe collection --- pmu/spe.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pmu/spe.cpp b/pmu/spe.cpp index f76dc3b..eeb77e1 100644 --- a/pmu/spe.cpp +++ b/pmu/spe.cpp @@ -62,6 +62,8 @@ static int OpenSpeEvent(PmuEvt *pmuAttr, int cpu) attr.sample_type = PERF_SAMPLE_TID; attr.sample_id_all = 1; attr.read_format = PERF_FORMAT_ID; + attr.exclude_kernel = pmuAttr->excludeKernel; + attr.exclude_user = pmuAttr->excludeUser; return PerfEventOpen(&attr, -1, cpu, -1, 0); } -- Gitee