diff --git a/docs/C_C++_API.md b/docs/C_C++_API.md index 933e9774bf008f5c86b5d0716bf252f842b5af64..c34322d7b3c3ee574d7d0a81d7e62a492468c014 100644 --- a/docs/C_C++_API.md +++ b/docs/C_C++_API.md @@ -87,6 +87,8 @@ 采集cgroup的名称列表 * unsigned numCgroup; 采集cgroup的个数 + * unsigned enableUserAccess + 是否直接读取寄存器,仅支持COUNTING模式 * 返回值 > 0 初始化成功 返回值 = -1 初始化失败,可通过Perror()查看错误信息 diff --git a/docs/Go_API.md b/docs/Go_API.md index 33eb0d7948db7100db9bbd0d399ee118eea57a66..5fbdaf83ed782c79fe93718687e6ddc63045999d 100644 --- a/docs/Go_API.md +++ b/docs/Go_API.md @@ -78,6 +78,8 @@ func PmuOpen(collectType C.enum_PmuTaskType, attr PmuAttr) (int, error) 采集cgroup的名称列表 * int numCgroup; 采集cgroup的个数 + * EnableUserAccess bool + 是否直接读取寄存器,仅支持COUNTING模式 * 返回值是int,error, 如果error不等于nil,则返回的int值为对应采集任务ID diff --git a/docs/Python_API.md b/docs/Python_API.md index 942257dfebcac7bcfeea730d0df34562609c587c..936ea4fea597e2e62affe7043a1af37a37d216ee 100644 --- a/docs/Python_API.md +++ b/docs/Python_API.md @@ -81,6 +81,8 @@ kperf.open(collector_type: kperf.PmuTaskType, pmu_attr: kperf.PmuAttr) 采集cgroup的名称列表 * numCgroup; 采集cgroup的个数 + * enableUserAccess + 是否直接读取寄存器,仅支持COUNTING模式 * 返回值是int值 fd > 0 成功初始化 fd == -1 初始化失败,可通过 kperf.error()查看错误信息 diff --git a/go/src/libkperf/kperf/kperf.go b/go/src/libkperf/kperf/kperf.go index b5f8a9b9f7102df9f3b653126c98a86d2df21c53..217e506ede68a822a1479d04fade8363a22c5a6f 100644 --- a/go/src/libkperf/kperf/kperf.go +++ b/go/src/libkperf/kperf/kperf.go @@ -76,6 +76,10 @@ void SetBlockedSample(struct PmuAttr* attr, unsigned blockedSample) { attr->blockedSample = blockedSample; } +void SetEnableUserAccess(struct PmuAttr* attr, unsigned enableUserAccess) { + attr->enableUserAccess = enableUserAccess; +} + struct PmuData* IPmuRead(int fd, int* len) { struct PmuData* pmuData = NULL; *len = PmuRead(fd, &pmuData); @@ -340,6 +344,7 @@ type PmuAttr struct { BranchSampleFilter uint64 // if the filter mode is set, branch_sample_stack data is collected in sampling mode BlockedSample bool // This indicates whether the blocked sample mode is enabled. In this mode, both on Cpu and off Cpu data is collected CgroupNameList []string // cgroup name list, if not user cgroup function, this field will be nullptr.if use cgroup function,use the cgroup name in the cgroupList to apply all event in the Event list + EnableUserAccess bool // enable user access counting for current process } type CpuTopology struct { @@ -583,6 +588,10 @@ func ToCPmuAttr(attr PmuAttr) (*C.struct_PmuAttr, int) { cAttr.symbolMode = attr.SymbolMode } + if attr.EnableUserAccess { + C.SetEnableUserAccess(cAttr, C.uint(1)) + } + return cAttr, 0 } diff --git a/go/src/libkperf_test/libkperf_test.go b/go/src/libkperf_test/libkperf_test.go index ff084296f92ae3b7b69156f4ad07a0c360a05137..c8c284ceb7f2e547ecfb6c3bfc94975f9562ccf1 100644 --- a/go/src/libkperf_test/libkperf_test.go +++ b/go/src/libkperf_test/libkperf_test.go @@ -31,6 +31,37 @@ func TestCount(t *testing.T) { kperf.PmuClose(fd) } +func TestUserAccessCount(t *testing.T) { + attr := kperf.PmuAttr{EvtList:[]string{"cycles"}, SymbolMode:kperf.ELF, PidList:[]int{0}, CpuList:[]int{-1}, EnableUserAccess:true} + fd, err := kperf.PmuOpen(kperf.COUNT, attr) + if err != nil { + t.Fatalf("kperf pmuopen counting failed, expect err is nil, but is %v", err) + } + kperf.PmuEnable(fd) + dataVo, err := kperf.PmuRead(fd) + if err != nil { + t.Fatalf("kperf pmuread failed, expect err is nil, but is %v", err) + } + for i, n := 0, 3; i < n; i++ { + j := 0; + for k := 0; k < 1000000000; k++ { + j = j + 1 + } + dataVo, err = kperf.PmuRead(fd) + if err != nil { + t.Fatalf("kperf pmuread failed, expect err is nil, but is %v", err) + } + for _, o := range dataVo.GoData { + t.Logf("================================Get Counting data success================================") + t.Logf("count base info comm=%v, evt=%v, pid=%v, tid=%v, coreId=%v, numaId=%v, sockedId=%v", o.Comm, o.Evt, o.Pid, o.Tid, o.CpuTopo.CoreId, o.CpuTopo.NumaId, o.CpuTopo.SocketId) + t.Logf("count info count=%v, countPercent=%v", o.Count, o.CountPercent) + } + kperf.PmuDataFree(dataVo) + } + kperf.PmuDisable(fd) + kperf.PmuClose(fd) +} + func TestSample(t *testing.T) { attr := kperf.PmuAttr{EvtList:[]string{"cycles"}, SymbolMode:kperf.ELF_DWARF, CallStack:true, SampleRate: 1000, UseFreq:true} fd, err := kperf.PmuOpen(kperf.SAMPLE, attr) diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index 5194dabaec88c6894a58c767ead9dd1e3259d055..63b06ee3c82aa0b8f199074584d5ca92552b0623 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -116,6 +116,7 @@ class CtypesPmuAttr(ctypes.Structure): ('branchSampleFilter', ctypes.c_ulong), ('cgroupNameList', ctypes.POINTER(ctypes.c_char_p)), ('numCgroup', ctypes.c_uint), + ('enableUserAccess', ctypes.c_uint, 1) ] def __init__(self, @@ -137,6 +138,7 @@ class CtypesPmuAttr(ctypes.Structure): branchSampleFilter=0, cgroupNameList=None, numCgroup=0, + enableUserAccess=False, *args, **kw): super(CtypesPmuAttr, self).__init__(*args, **kw) @@ -198,6 +200,7 @@ class CtypesPmuAttr(ctypes.Structure): self.callStack = callStack self.blockedSample = blockedSample self.includeNewFork = includeNewFork + self.enableUserAccess = enableUserAccess class PmuAttr(object): @@ -220,7 +223,8 @@ class PmuAttr(object): minLatency=0, includeNewFork=False, branchSampleFilter=0, - cgroupNameList=None): + cgroupNameList=None, + enableUserAccess=False): self.__c_pmu_attr = CtypesPmuAttr( evtList=evtList, @@ -239,9 +243,18 @@ class PmuAttr(object): minLatency=minLatency, includeNewFork=includeNewFork, branchSampleFilter=branchSampleFilter, - cgroupNameList=cgroupNameList + cgroupNameList=cgroupNameList, + enableUserAccess=enableUserAccess ) + @property + def enableUserAccess(self): + return bool(self.c_pmu_attr.enableUserAccess) + + @enableUserAccess.setter + def enableUserAccess(self, enableUserAccess): + self.c_pmu_attr.enableUserAccess = int(enableUserAccess) + @property def c_pmu_attr(self): return self.__c_pmu_attr diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py index 285e5d792e5a0f1326cde3222c40a50875058ed1..1efaabd4324aea7faa4299bdc7fae31c5a95dc11 100644 --- a/python/modules/kperf/perror.py +++ b/python/modules/kperf/perror.py @@ -117,6 +117,10 @@ class Error: LIBPERF_ERR_INVALID_PMU_FILE = 1071 LIBPERF_ERR_NOT_SUPPORT_PCIE_PORT = 1072 LIBPERF_ERR_INVALID_EVTATTR = 1073 + LIBPERF_ERR_COUNT_MMAP_IS_NULL = 1074 + LIBPERF_ERR_ENABLE_USER_ACCESS_FAILED = 1075 + LIBPERF_ERR_ALLOCATE_REGISTER_FAILED = 1076 + LIBPERF_ERR_CHECK_USER_ACCESS = 1077 UNKNOWN_ERROR = 9999 diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py index 8840038867e25cca7fae7eef2cf1ae2c1ad26bc2..d397e1632198ea14d08096a19260741012c9fa8c 100644 --- a/python/modules/kperf/pmu.py +++ b/python/modules/kperf/pmu.py @@ -283,7 +283,8 @@ class PmuAttr(_libkperf.PmuAttr): minLatency = 0, includeNewFork = False, branchSampleFilter = 0, - cgroupNameList = None): + cgroupNameList = None, + enableUserAccess = False): super(PmuAttr, self).__init__( evtList=evtList, pidList=pidList, @@ -301,7 +302,8 @@ class PmuAttr(_libkperf.PmuAttr): minLatency=minLatency, includeNewFork=includeNewFork, branchSampleFilter=branchSampleFilter, - cgroupNameList=cgroupNameList + cgroupNameList=cgroupNameList, + enableUserAccess=enableUserAccess, ) diff --git a/python/tests/test_counting.py b/python/tests/test_counting.py index 674d75a483e9cf6ca43c860c9b0f7b0046826234..18021eae8b890c300b208a360847d69f822ef5ec 100644 --- a/python/tests/test_counting.py +++ b/python/tests/test_counting.py @@ -43,6 +43,30 @@ def test_event_counting(): kperf.disable(pd) # Ensure PMU is disabled after the test kperf.close(pd) # Close the PMU descriptor +def test_user_access_counting(): + evtList = ["cycles"] + pidList = [0] + cpuList = [-1] + pmu_attr = kperf.PmuAttr(evtList=evtList, pidList=pidList, cpuList=cpuList, enableUserAccess=True) + pd = kperf.open(kperf.PmuTaskType.COUNTING, pmu_attr) + if pd == -1: + pytest.fail(f"Failed to open PMU: {kperf.error()}") + kperf.enable(pd) + evtMap = read_event_counts(pd) + for _ in range(3): + i = 10000 + while i > 0: + i = i - 1 + evtMap = read_event_counts(pd) + assert len(evtMap) > 0, "No event counts were captured" + for evt, count in evtMap.items(): + assert isinstance(evt, str), f"Invalid event name: {evt}" + assert isinstance(count, int), f"Invalid count for event {evt}: {count}" + for evt, count in evtMap.items(): + print(f"evt:{evt} count:{count}") + kperf.disable(pd) + kperf.close(pd) + if __name__ == '__main__': # 提示用户使用pytest 运行测试文件 print("This is a pytest script. Run it using the 'pytest' command.")