diff --git a/go/src/libkperf/kperf/kperf.go b/go/src/libkperf/kperf/kperf.go index 7d5def1a16e1bcd9f029de1a552245ae4d873cb6..025cc361161fa7eb37e61dcf4a5dacb9bca694d6 100644 --- a/go/src/libkperf/kperf/kperf.go +++ b/go/src/libkperf/kperf/kperf.go @@ -178,6 +178,10 @@ size_t GetPmuTraceAttrSize() { return sizeof(struct PmuTraceAttr); } +size_t GetPmuHwMetricAttrSize() { + return sizeof(struct PmuHwMetricAttr); +} + void SetExcludeUserForEvt(struct EvtAttr* attr, unsigned excludeUser) { attr->excludeUser = excludeUser; } @@ -359,7 +363,20 @@ var ( PMU_METRIC_BDF C.enum_PmuMetricMode = C.PMU_METRIC_BDF PMU_METRIC_CHANNEL C.enum_PmuMetricMode = C.PMU_METRIC_CHANNEL ) - + +// PmuHwMetric +var ( + PMU_HWM_CPI C.enum_PmuHwMetric = C.PMU_HWM_CPI + PMU_HWM_L3_CACHE_MISS C.enum_PmuHwMetric = C.PMU_HWM_L3_CACHE_MISS + PMU_HWM_L3_LOAD_MISS C.enum_PmuHwMetric = C.PMU_HWM_L3_LOAD_MISS + PMU_HWM_L1_DCACHE_MISS C.enum_PmuHwMetric = C.PMU_HWM_L1_DCACHE_MISS + PMU_HWM_L1_ICACHE_LOAD_MISS C.enum_PmuHwMetric = C.PMU_HWM_L1_ICACHE_LOAD_MISS + PMU_HWM_DTLB_LOAD_MISS C.enum_PmuHwMetric = C.PMU_HWM_DTLB_LOAD_MISS + PMU_HWM_ITLD_LOAD_MISS C.enum_PmuHwMetric = C.PMU_HWM_ITLD_LOAD_MISS + PMU_HWM_BRACH_LOADS_MISS C.enum_PmuHwMetric = C.PMU_HWM_BRACH_LOADS_MISS + PMU_HWM_L2D_CACHE_MISS C.enum_PmuHwMetric = C.PMU_HWM_L2D_CACHE_MISS +) + var fdModeMap map[int]C.enum_PmuTaskType = make(map[int]C.enum_PmuTaskType) type EvtAttr struct { @@ -514,6 +531,13 @@ type PmuCpuFreqDetail struct { AvgFreq uint64 // average frequency of core } +type PmuHwMetricAttr struct { + Metric C.enum_PmuHwMetric + BasePeriod uint32 + Threshold float64 + Pid uint32 +} + func FreePmuAttr(attr *C.struct_PmuAttr) { if attr == nil { return @@ -1435,3 +1459,24 @@ func PmuWriteData(file C.PmuFile, dataVo PmuDataVo) error { func PmuEndWrite(file C.PmuFile) { C.PmuEndWrite(file) } + +// pmu open with hw_metric +func PmuOpenWithHwMetric(hwMetricAttr PmuHwMetricAttr) (int, error) { + attrSize := C.GetPmuHwMetricAttrSize() + ptr := C.malloc(C.size_t(int(attrSize))) + if ptr == nil { + return -1, errors.New("malloc failed") + } + defer C.free(ptr) + C.memset(ptr, 0, attrSize) + cAttr := (*C.struct_PmuHwMetricAttr)(ptr) + cAttr.metric = hwMetricAttr.Metric + cAttr.basePeriod = C.uint(hwMetricAttr.BasePeriod) + cAttr.threshold = C.double(hwMetricAttr.Threshold) + cAttr.pid = C.uint(hwMetricAttr.Pid) + taskId := C.PmuOpenWithHWMetric(cAttr) + if int(taskId) == -1 { + return -1, errors.New(C.GoString(C.Perror())) + } + return int(taskId), nil +} diff --git a/go/src/libkperf_test/libkperf_test.go b/go/src/libkperf_test/libkperf_test.go index 9b4d63947c1caf08346cc3d575bfe3bb6e2f1104..8cac03be02ff4ef7b140e6db2774db06bfc83481 100644 --- a/go/src/libkperf_test/libkperf_test.go +++ b/go/src/libkperf_test/libkperf_test.go @@ -411,4 +411,24 @@ func TestCgroupNameList(t *testing.T) { } kperf.PmuDataFree(dataVo) kperf.PmuClose(fd) -} \ No newline at end of file +} + +func TestHwMetric(t *testing.T) { + attr := kperf.PmuHwMetricAttr{Metric:kperf.PMU_HWM_CPI, BasePeriod: 1000000, Threshold:1.25} + pd, err := kperf.PmuOpenWithHwMetric(attr) + if err != nil { + t.Fatalf("kperf PmuOpenWithHwMetric failed, expect err is nil, but is %v", err) + } + kperf.PmuEnable(pd) + time.Sleep(time.Second) + kperf.PmuDisable(pd) + + dataVo, err := kperf.PmuRead(pd) + if err != nil { + t.Fatalf("kperf PmuRead failed, expect err is nil, but is %v", err) + } + + for _, o := range dataVo.GoData { + t.Logf("sample base info comm=%v,pid=%v,tid=%v,evt=%v, period=%v,hw_metric=1", o.Comm,o.Pid,o.Tid,o.Evt, o.Period) + } +} diff --git a/include/pcerrc.h b/include/pcerrc.h index a1166124ec0121ba284e84dd387910e6c4d6fc5a..bebe2353f60b8634c8c3b5dcf3f0d8f529014724 100644 --- a/include/pcerrc.h +++ b/include/pcerrc.h @@ -127,6 +127,8 @@ extern "C" { #define LIBPERF_ERR_INVALID_BPF_PARAM 1080 #define LIBPERF_ERR_NULL_POINTER 1081 #define LIBPERF_ERR_BUFFER_CORRUPTED 1082 +#define LIBPERF_ERR_NOT_SUPPORT_HWMETRIC 1083 +#define LIBPERF_ERR_INVALID_HWMETRIC_ATTR 1084 #define UNKNOWN_ERROR 9999 diff --git a/include/pmu.h b/include/pmu.h index 20aa9892cadeb10ee344f0da0d815fafe8eaa335..1bc8b277929c1d14971980aee810962a502601c2 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -753,6 +753,27 @@ int PmuWriteData(PmuFile file, struct PmuData *data, int len); */ void PmuEndWrite(PmuFile file); +enum PmuHwMetric { + PMU_HWM_CPI, + PMU_HWM_L3_CACHE_MISS, + PMU_HWM_L3_LOAD_MISS, + PMU_HWM_L1_DCACHE_MISS, + PMU_HWM_L1_ICACHE_LOAD_MISS, + PMU_HWM_DTLB_LOAD_MISS, + PMU_HWM_ITLD_LOAD_MISS, + PMU_HWM_BRACH_LOADS_MISS, + PMU_HWM_L2D_CACHE_MISS, +}; + +struct PmuHwMetricAttr { + enum PmuHwMetric metric; + unsigned basePeriod; + double threshold; + unsigned pid; +}; + +int PmuOpenWithHWMetric(struct PmuHwMetricAttr* hwMetricAttr); + #pragma GCC visibility pop #ifdef __cplusplus } diff --git a/pmu/pmu.cpp b/pmu/pmu.cpp index 59b35a3f6da4798ca3b969367c61652ed1b1bd25..259a3b99216eaf1c0b188c4f172df51db90020a2 100644 --- a/pmu/pmu.cpp +++ b/pmu/pmu.cpp @@ -310,6 +310,24 @@ static int CheckBpfMode(enum PmuTaskType collectType, struct PmuAttr *attr) return SUCCESS; } +static int CheckHwMetric(enum PmuTaskType collectType, struct PmuAttr* attr) { + if (!attr->enableHwMetric) { + return SUCCESS; + } + + if (collectType != SAMPLING) { + New(LIBPERF_ERR_NOT_SUPPORT_HWMETRIC, "just sampling mode support hw metric"); + return LIBPERF_ERR_NOT_SUPPORT_HWMETRIC; + } + + if (!CheckCurKernelConfig("CONFIG_HISILICON_HW_METRIC=y")) { + New(LIBPERF_ERR_NOT_SUPPORT_HWMETRIC, "Current kernel can't support hw metric, Please upgrade the kernel version to 6.6.0-108"); + return LIBPERF_ERR_NOT_SUPPORT_HWMETRIC; + } + + return SUCCESS; +} + static int CheckAttr(enum PmuTaskType collectType, struct PmuAttr *attr) { auto err = CheckUserAccess(collectType, attr); @@ -360,6 +378,12 @@ static int CheckAttr(enum PmuTaskType collectType, struct PmuAttr *attr) New(err); return err; } + + err = CheckHwMetric(collectType, attr); + if (err != SUCCESS) { + return err; + } + return SUCCESS; } diff --git a/pmu/pmu_metric.cpp b/pmu/pmu_metric.cpp index 82232d2640a4827f0e3b51a159af101c35bf00c5..97a09dccc837ea19d3533fe8ecbcb47b640b079a 100644 --- a/pmu/pmu_metric.cpp +++ b/pmu/pmu_metric.cpp @@ -1873,3 +1873,60 @@ int PmuGetNumaCore(unsigned nodeId, unsigned **coreList) return -1; } } + +static map> HW_METRIC_MAP = { + {PmuHwMetric::PMU_HWM_CPI, {"armv8_pmuv3_0/inst_retired/", "armv8_pmuv3_0/cpu_cycles/"}}, + {PmuHwMetric::PMU_HWM_L3_CACHE_MISS, {"armv8_pmuv3_0/ll_cache/", "armv8_pmuv3_0/ll_cache_miss/"}}, + {PmuHwMetric::PMU_HWM_L3_LOAD_MISS, {"LLC-loads", "LLC-load-misses"}}, + {PmuHwMetric::PMU_HWM_L1_DCACHE_MISS, {"L1-dcache-loads", "L1-dcache-load-misses"}}, + {PmuHwMetric::PMU_HWM_L1_ICACHE_LOAD_MISS, {"L1-icache-loads", "L1-icache-load-misses"}}, + {PmuHwMetric::PMU_HWM_DTLB_LOAD_MISS, {"dTLB-loads", "dTLB-load-misses"}}, + {PmuHwMetric::PMU_HWM_ITLD_LOAD_MISS, {"iTLB-loads", "iTLB-load-misses"}}, + {PmuHwMetric::PMU_HWM_BRACH_LOADS_MISS, {"branch-loads", "branch-load-misses"}}, + {PmuHwMetric::PMU_HWM_L2D_CACHE_MISS, {"armv8_pmuv3_0/l2d_cache/", "armv8_pmuv3_0/l2d_cache_refill/"}}, +}; + +int PmuOpenWithHWMetric(struct PmuHwMetricAttr* hwMetricAttr) { +#ifdef IS_X86 + New(LIBPERF_ERR_INTERFACE_NOT_SUPPORT_X86); + return -1; +#else + if (hwMetricAttr->threshold == 0) { + New(LIBPERF_ERR_INVALID_HWMETRIC_ATTR, "Invalid threshold which must can't be zero"); + return -1; + } + + if (HW_METRIC_MAP.find(hwMetricAttr->metric) == HW_METRIC_MAP.end()) { + New(LIBPERF_ERR_INVALID_HWMETRIC_ATTR, "Invalid metric not found in HwMetric"); + return -1; + } + + if (!hwMetricAttr->basePeriod) { + New(LIBPERF_ERR_INVALID_HWMETRIC_ATTR, "Invalid base period which can't be zero"); + return -1; + } + + PmuAttr attr = {0}; + char* evtList[2]; + evtList[0] = HW_METRIC_MAP[hwMetricAttr->metric][0]; + evtList[1] = HW_METRIC_MAP[hwMetricAttr->metric][1]; + + if (hwMetricAttr->pid > 0) { + int pidList[1] = {hwMetricAttr->pid}; + attr.pidList = pidList; + attr.numPid = 1; + } + + EvtAttr evtAttr[2]; + auto dstPeriod = static_cast (hwMetricAttr->basePeriod * hwMetricAttr->threshold); + evtAttr[0] = {1, hwMetricAttr->basePeriod, false, false}; + evtAttr[1] = {1, dstPeriod, false, false}; + attr.evtAttr = evtAttr; + attr.evtList = evtList; + attr.numEvt = 2; + attr.numGroup = 2; + attr.enableHwMetric = 1; + int pd = PmuOpen(SAMPLING, &attr); + return pd; +#endif +} diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index 53ded34cae26fccdebb25ba2886a3b0b6e13df53..0394a76983e5d6c7b195ff65754388c463db67f2 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -2303,6 +2303,97 @@ def PmuEndWrite(file): return c_func(file) +class CtypesPmuHwMetricAttr(ctypes.Structure): + """ + struct PmuDeviceAttr { + enum PmuHwMetric metric; + unsigned basePeriod; + double threshold; + }; + """ + _fields_ = [ + ('metric', ctypes.c_int), + ('basePeriod', ctypes.c_uint), + ('threshold', ctypes.c_double), + ('pid', ctypes.c_uint), + ] + + def __init__(self, + metric=0, + basePeriod=1000000, + threshold=1.25, + pid=0, + *args, **kw): + super(CtypesPmuHwMetricAttr, self).__init__(*args, **kw) + + self.metric = ctypes.c_int(metric) + self.basePeriod = ctypes.c_uint(basePeriod) + self.threshold = ctypes.c_double(threshold) + self.pid = ctypes.c_uint(pid) + +class PmuHwMetricAttr: + __slots__ = ['__c_hw_metric_attr'] + + def __init__(self, + metric=0, + basePeriod=0, + threshold=0, + pid=0): + self.__c_hw_metric_attr = CtypesPmuHwMetricAttr( + metric=metric, + basePeriod=basePeriod, + threshold=threshold, + pid=pid + ) + + @property + def c_hw_metric_attr(self): + return self.__c_hw_metric_attr + + @property + def metric(self): + return self.c_hw_metric_attr.metric + + @metric.setter + def metric(self, metric): + self.c_hw_metric_attr.metric = ctypes.c_int(metric) + + @property + def basePeriod(self): + return self.c_hw_metric_attr.basePeriod + + @basePeriod.setter + def basePeriod(self, basePeriod): + self.c_hw_metric_attr.basePeriod = ctypes.c_uint(basePeriod) + + @property + def pid(self): + return self.c_hw_metric_attr.pid + + @pid.setter + def pid(self, pid): + self.c_hw_metric_attr.pid = ctypes.c_uint(pid) + + @property + def threshold(self): + return self.c_cpu_topo.socketId + + @threshold.setter + def threshold(self, threshold): + self.c_hw_metric_attr.threshold = ctypes.c_double(threshold) + + @classmethod + def from_c_hw_metric_attr(cls, c_hw_metric_attr): + hw_metric_attr = cls() + hw_metric_attr.__c_hw_metric_attr = c_hw_metric_attr + return hw_metric_attr + +def PmuOpenWithHWMetric(hwMetricAttr): + c_PmuOpenWithHWMetric = kperf_so.PmuOpenWithHWMetric + c_PmuOpenWithHWMetric.argtypes = [ctypes.POINTER(CtypesPmuHwMetricAttr)] + c_PmuOpenWithHWMetric.restype = ctypes.c_int + return c_PmuOpenWithHWMetric(ctypes.byref(hwMetricAttr.c_hw_metric_attr)) + __all__ = [ 'CtypesEvtAttr', 'EvtAttr', @@ -2356,5 +2447,7 @@ __all__ = [ 'ResolvePmuDataSymbol', 'PmuBeginWrite', 'PmuWriteData', - 'PmuEndWrite' + 'PmuEndWrite', + 'PmuOpenWithHWMetric', + 'PmuHwMetricAttr', ] diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py index f0dec93bd08d8be897d8fc253d9a6c9c38edc6a3..ccdaba9aae1c22a1a621a2b4bdbaa31a292bb166 100644 --- a/python/modules/kperf/perror.py +++ b/python/modules/kperf/perror.py @@ -126,6 +126,8 @@ class Error: LIBPERF_ERR_INVALID_BPF_PARAM = 1080 LIBPERF_ERR_NULL_POINTER = 1081 LIBPERF_ERR_BUFFER_CORRUPTED = 1082 + LIBPERF_ERR_NOT_SUPPORT_HWMETRIC = 1083 + LIBPERF_ERR_INVALID_HWMETRIC_ATTR = 1084 UNKNOWN_ERROR = 9999 diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index 8f6b65726b2600cd8278ce6e20963402b95fd6f6..9b7fe4efdab7cb63cd314b9bad61459f6d5b0a81 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -177,6 +177,33 @@ class PmuDeviceMetric: # Collect rate of cross-socket operations received by HHA. PMU_HHA_CROSS_SOCKET = 15 +class PmuHwMetric: + PMU_HWM_CPI = 0 + PMU_HWM_L3_CACHE_MISS = 1 + PMU_HWM_L3_LOAD_MISS = 2 + PMU_HWM_L1_DCACHE_MISS = 3 + PMU_HWM_L1_ICACHE_LOAD_MISS = 4 + PMU_HWM_DTLB_LOAD_MISS = 5 + PMU_HWM_ITLD_LOAD_MISS = 6 + PMU_HWM_BRACH_LOADS_MISS = 7 + PMU_HWM_L2D_CACHE_MISS = 8 + +class PmuHwMetricAttr(_libkperf.PmuHwMetricAttr): + """ + struct PmuHwMetricAttr { + enum PmuHwMetric metric; + unsigned basePeriod; + double threshold; + }; + """ + def __init__(self, metric, basePeriod=1000000, threshold=1.25, pid=0): + super(PmuHwMetricAttr, self).__init__( + metric=metric, + basePeriod=basePeriod, + threshold=threshold, + pid=pid + ) + class PmuDeviceAttr(_libkperf.PmuDeviceAttr): """ struct PmuDeviceAttr { @@ -631,6 +658,9 @@ def write_data(file, data): def end_write(file): return _libkperf.PmuEndWrite(file) +def pmu_open_with_hw_metric(pmu_hw_metric_attr): + return _libkperf.PmuOpenWithHWMetric(pmu_hw_metric_attr) + __all__ = [ 'PmuTaskType', 'PmuEventType', @@ -687,5 +717,8 @@ __all__ = [ 'resolvePmuDataSymbol', 'begin_write', 'write_data', - 'end_write' + 'end_write', + 'PmuHwMetric', + 'PmuHwMetricAttr', + 'pmu_open_with_hw_metric', ] diff --git a/test/test_perf/test_metric.cpp b/test/test_perf/test_metric.cpp index 315bee994cfcf4415b05c2ee4f3c5d376bb08a8d..1d1064c6d9648615b54f1da7d7e6d2b3e6678413 100644 --- a/test/test_perf/test_metric.cpp +++ b/test/test_perf/test_metric.cpp @@ -13,6 +13,7 @@ * Description: Unit test for metric. ******************************************************************************/ #include "test_common.h" +#include "common.h" #include "cpu_map.h" #include @@ -369,4 +370,30 @@ TEST_F(TestMetric, GetMetricPcieLatency) DevDataFree(devData); PmuDataFree(oriData); PmuClose(pd); +} + +TEST_F(TestMetric, TestHwMetric) +{ + CHIP_TYPE chipType = GetCpuType(); + if (chipType != HIPG) { + GTEST_SKIP() << "Unsupported chip"; + } + + if (!CheckCurKernelConfig("CONFIG_HISILICON_HW_METRIC=y")) { + GTEST_SKIP() << "Current kernel can't support hw metric"; + } + + PmuHwMetricAttr attr = {PmuHwMetric::PMU_HWM_CPI, 1000000, 0.8}; + int pd = PmuOpenWithHWMetric(&attr); + ASSERT_NE(pd, -1); + PmuEnable(pd); + sleep(1); + PmuDisable(pd); + PmuData* oriData = nullptr; + int oriLen = PmuRead(pd, &oriData); + ASSERT_NE(oriLen, -1); + for (int i = 0; i < oriLen; i++) { + ASSERT_EQ(oriData[i].period, 800000); + } + PmuClose(pd); } \ No newline at end of file diff --git a/util/common.cpp b/util/common.cpp index 7d2e23493e6ad716616cfdd6ede001ae7df57d64..f24e528ff5ba579cd592c32acee42c9181f29807 100644 --- a/util/common.cpp +++ b/util/common.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -187,3 +188,27 @@ std::string GetCgroupPath(const std::string& cgroupName) { } return cgroupRootPath; } + +bool CheckCurKernelConfig(const std::string& configName) +{ + std::string configPath; + struct utsname buff; + if (uname(&buff) == 0) { + configPath = KERNEL_CONFIG_BASE_PATH + buff.release; + } else { + return false; + } + + std::ifstream configFile(configPath.c_str()); + if (!configFile.is_open()) { + return false; + } + + std::string line; + while(getline(configFile, line)) { + if (line.find(configName) != std::string::npos) { + return true; + } + } + return false; +} diff --git a/util/common.h b/util/common.h index e3d3bab9db239229e260b449a91025004fd7805a..4f9a8ea8a9d9186223db0be60bdbee9a9b95f689 100644 --- a/util/common.h +++ b/util/common.h @@ -36,6 +36,7 @@ const std::string TRACE_EVENT_PATH = "/sys/kernel/tracing/events/"; const std::string TRACE_DEBUG_EVENT_PATH = "/sys/kernel/debug/tracing/events/"; const std::string PERF_EVENT_PARANOID_PATH = "/proc/sys/kernel/perf_event_paranoid"; const std::string SYS_DEVICE_PATH = "/sys/bus/event_source/devices/"; +const std::string KERNEL_CONFIG_BASE_PATH = "/boot/config-"; inline bool IsValidIp(unsigned long ip) { @@ -56,5 +57,6 @@ std::string GetTraceEventDir(); bool ConvertStrToInt(const std::string& intValStr, int32_t& val); int GetParanoidVal(); std::string GetCgroupPath(const std::string& cgroupName); +bool CheckCurKernelConfig(const std::string& configName); #endif // LIBKPROF_COMMON_H