From bc8397165b24d48673e98dc622ea4d99571f7413 Mon Sep 17 00:00:00 2001 From: victorjin Date: Thu, 16 May 2024 20:17:58 +0800 Subject: [PATCH 1/6] =?UTF-8?q?=E5=AE=9E=E7=8E=B0libkperf=20python?= =?UTF-8?q?=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + CMakeLists.txt | 5 + README.en.md | 54 ++- README.md | 56 ++- build.sh | 9 +- include/pmu.h | 9 +- python/CMakeLists.txt | 17 + python/modules/CMakeLists.txt | 29 ++ python/modules/__init__.py | 14 + python/modules/_libkperf/Config.py | 42 ++ python/modules/_libkperf/Perror.py | 31 ++ python/modules/_libkperf/Pmu.py | 668 +++++++++++++++++++++++++++ python/modules/_libkperf/Symbol.py | 405 ++++++++++++++++ python/modules/_libkperf/__init__.py | 24 + python/modules/kperf/__init__.py | 16 + python/modules/kperf/perror.py | 29 ++ python/modules/kperf/pmu.py | 127 +++++ python/modules/ksym/__init__.py | 15 + python/modules/ksym/symbol.py | 59 +++ python/modules/setup.py.in | 26 ++ 20 files changed, 1626 insertions(+), 10 deletions(-) create mode 100644 .gitignore create mode 100644 python/CMakeLists.txt create mode 100644 python/modules/CMakeLists.txt create mode 100644 python/modules/__init__.py create mode 100644 python/modules/_libkperf/Config.py create mode 100644 python/modules/_libkperf/Perror.py create mode 100644 python/modules/_libkperf/Pmu.py create mode 100644 python/modules/_libkperf/Symbol.py create mode 100644 python/modules/_libkperf/__init__.py create mode 100644 python/modules/kperf/__init__.py create mode 100644 python/modules/kperf/perror.py create mode 100644 python/modules/kperf/pmu.py create mode 100644 python/modules/ksym/__init__.py create mode 100644 python/modules/ksym/symbol.py create mode 100644 python/modules/setup.py.in diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..485dee6 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea diff --git a/CMakeLists.txt b/CMakeLists.txt index 410a581..dec775a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,11 @@ include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) add_subdirectory(symbol) add_subdirectory(pmu) +option(PYTHON "Build python module" OFF) +if (PYTHON) + add_subdirectory(python) +endif() + if (INCLUDE_TEST) add_subdirectory(test) endif() diff --git a/README.en.md b/README.en.md index 84e8380..4b63a2f 100644 --- a/README.en.md +++ b/README.en.md @@ -16,10 +16,20 @@ Symbol resolve module is developed on elfin-parser, a library for parsing elf an Run bash script: ```sh -sh build.sh installPath=/home/libkperf +bash build.sh installPath=/home/libkperf ``` As mentioned above, the header and library will be installed in the/home/libkperf output directory, and installPath is an optional parameter. If not set, it will be installed in the output directory under libkperf by default. +If you want to add additional python library support, you can install it as follows: +```shell +bash build.sh python +``` + +If you need to uninstall the python library after installation, you can run the following command: +```shell +python -m pip uninstall -y libkperf +``` + #### Instructions All pmu functions are accomplished by the following interfaces: * PmuOpen @@ -68,6 +78,48 @@ PmuDataFree(data); PmuClose(pd); ``` +Python examples: +```python +import time +from collections import defaultdict + +import kperf + +def Counting(): + evtList = ["r11", "cycles"] + pmu_attr = kperf.PmuAttr(evtList=evtList) + pd = kperf.open(kperf.PmuTaskType.COUNTING, pmu_attr) + if pd == -1: + print(kperf.errorno()) + print(kperf.error()) + + kperf.enable(pd) + + for _ in range(3): + time.sleep(1) + data_iter = kperf.read(pd) + evtMap = defaultdict(int) + for data in data_iter: + evtMap[data.evt] += data.count + + for evt, count in evtMap.items(): + print(f"event: {evt} count: {count}") + + kperf.disable(pd) + kperf.close(pd) + + +def PerfList(): + event_iter = kperf.event_list(kperf.PmuEventType.CORE_EVENT) + for event in event_iter: + print(f"event: {event}") + + +if __name__ == '__main__': + Counting() + PerfList() +``` + #### Contribution diff --git a/README.md b/README.md index e2af003..ba61bd2 100644 --- a/README.md +++ b/README.md @@ -18,11 +18,21 @@ Pmu收集模块是在syscall perf_event_open上开发的,用于启用内核pmu 运行bash脚本: +```shell +bash build.sh installPath=/home/libkperf ``` -sh build.sh installPath=/home/libkperf + +如上,头文件和库将安装到/home/libkperf输出目录,installPath是可选参数,若没有设置,则默认安装到libkperf下的output目录。 + +如果要额外增加python库支持,可以通过如下方式安装 +```shell +bash build.sh PYTHON=true ``` -如上,标头和库将安装到/home/libkperf输出目录,installPath是可选参数,若没有设置,则默认安装到libkperf下的output目录。 +安装后若需要卸载python库, 可以执行下述命令 +```shell +python3 -m pip unistall -y libkperf +``` #### 指令 @@ -76,6 +86,48 @@ PmuDataFree(data); PmuClose(pd); ``` +Python 例子: +```python +import time +from collections import defaultdict + +import kperf + +def Counting(): + evtList = ["r11", "cycles"] + pmu_attr = kperf.PmuAttr(evtList=evtList) + pd = kperf.open(kperf.PmuTaskType.COUNTING, pmu_attr) + if pd == -1: + print(kperf.errorno()) + print(kperf.error()) + + kperf.enable(pd) + + for _ in range(3): + time.sleep(1) + data_iter = kperf.read(pd) + evtMap = defaultdict(int) + for data in data_iter: + evtMap[data.evt] += data.count + + for evt, count in evtMap.items(): + print(f"event: {evt} count: {count}") + + kperf.disable(pd) + kperf.close(pd) + + +def PerfList(): + event_iter = kperf.event_list(kperf.PmuEventType.CORE_EVENT) + for event in event_iter: + print(f"event: {event}") + + +if __name__ == '__main__': + Counting() + PerfList() +``` + #### 参与贡献 1. Fork 本仓库 diff --git a/build.sh b/build.sh index fe428c2..27e60cb 100644 --- a/build.sh +++ b/build.sh @@ -21,6 +21,8 @@ BUILD_DIR=${PROJECT_DIR}/_build THIRD_PARTY=${PROJECT_DIR}/third_party/ INSTALL_PATH=${PROJECT_DIR}/output/ BUILD_TYPE=Release +# Python module are not compiled by default. +PYTHON=false # Test cases are not compiled by default. INCLUDE_TEST=false @@ -42,6 +44,9 @@ for arg in "$@"; do test=*) INCLUDE_TEST="${arg#*=}" ;; + python=*) + PYTHON="${arg#*=}" + ;; installPath=*) INSTALL_PATH="${arg#*=}" ;; @@ -81,10 +86,10 @@ function build_elfin() { build_libprof() { cd $BUILD_DIR - cmake -DINCLUDE_TEST=${INCLUDE_TEST} -DCMAKE_INSTALL_PREFIX=${INSTALL_PATH} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. + cmake -DINCLUDE_TEST=${INCLUDE_TEST} -DPYTHON=${PYTHON} -DCMAKE_INSTALL_PREFIX=${INSTALL_PATH} -DCMAKE_BUILD_TYPE=${BUILD_TYPE} .. make -j ${cpu_core_num} make install - echo "build_libkperf success" + echo "build libkperf success" } function build_test() diff --git a/include/pmu.h b/include/pmu.h index 715f48e..55cec41 100644 --- a/include/pmu.h +++ b/include/pmu.h @@ -117,7 +117,7 @@ struct PmuDataExt { }; struct PmuData { - struct Stack* stack; // call stack + struct Stack* stack; // call stack const char *evt; // event name int64_t ts; // time stamp pid_t pid; // process id @@ -126,10 +126,9 @@ struct PmuData { 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. - }; + uint64_t count; // event count. Only available for Counting. + struct PmuDataExt *ext; // extension. Only available for Spe. + }; /** diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt new file mode 100644 index 0000000..5c188ab --- /dev/null +++ b/python/CMakeLists.txt @@ -0,0 +1,17 @@ +# 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: Victor Jin +# Create: 2024-05-16 +# Description: libkperf python module +project(python_libkperf) +find_package(PythonInterp REQUIRED) +find_package(PythonLibs REQUIRED) + +add_subdirectory(modules) \ No newline at end of file diff --git a/python/modules/CMakeLists.txt b/python/modules/CMakeLists.txt new file mode 100644 index 0000000..5cf582c --- /dev/null +++ b/python/modules/CMakeLists.txt @@ -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: Victor Jin +# Create: 2024-05-16 +# Description: libkperf python modules cmake + +project(python_libkperf) + +set(LIBKPERF_PATH ${CMAKE_BINARY_DIR}/pmu/libkperf.so) +set(LIBSYM_PATH ${CMAKE_BINARY_DIR}/symbol/libsym.so) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/setup.py.in + ${CMAKE_CURRENT_SOURCE_DIR}/setup.py +) + +add_custom_target(${PROJECT_NAME} ALL + COMMAND ${PYTHON_EXECUTABLE} setup.py install + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +add_dependencies(${PROJECT_NAME} kperf sym) \ No newline at end of file diff --git a/python/modules/__init__.py b/python/modules/__init__.py new file mode 100644 index 0000000..725583e --- /dev/null +++ b/python/modules/__init__.py @@ -0,0 +1,14 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: python ksym module +""" \ No newline at end of file diff --git a/python/modules/_libkperf/Config.py b/python/modules/_libkperf/Config.py new file mode 100644 index 0000000..1468fe9 --- /dev/null +++ b/python/modules/_libkperf/Config.py @@ -0,0 +1,42 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: ctype python Config module +""" +import os +import ctypes +from ctypes.util import find_library + +VERSION_MAJOR = 1 +VERSION_MINOR = 0 +VERSION_BUGFIX = 0 +VERSION_SUFFIX = '' +VERSION = '1.0' + +UTF_8 = 'utf-8' + +def lib_path(): + return os.path.dirname(os.path.abspath(__file__)) + + +def libsym_path(): + libsym = 'libsym.so' + return os.path.join(lib_path(), libsym) + + +def libkperf_path(): + libkperf = 'libkperf.so' + return os.path.join(lib_path(), libkperf) + + +sym_so = ctypes.CDLL(libsym_path()) +kperf_so = ctypes.CDLL(find_library(libkperf_path())) \ No newline at end of file diff --git a/python/modules/_libkperf/Perror.py b/python/modules/_libkperf/Perror.py new file mode 100644 index 0000000..bc442c1 --- /dev/null +++ b/python/modules/_libkperf/Perror.py @@ -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: Victor Jin +Create: 2024-05-10 +Description: ctype python Perror module +""" +import ctypes + +from .Config import UTF_8, kperf_so + + +def Perrorno() -> int: + c_Perrorno = kperf_so.Perrorno + c_Perrorno.argtypes = [] + c_Perrorno.restype = ctypes.c_int + return c_Perrorno() + + +def Perror() -> str: + c_Perror = kperf_so.Perror + c_Perror.argtypes = [] + c_Perror.restype = ctypes.c_char_p + return c_Perror().decode(UTF_8) diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py new file mode 100644 index 0000000..00dc466 --- /dev/null +++ b/python/modules/_libkperf/Pmu.py @@ -0,0 +1,668 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: ctype python Pmu module +""" +import ctypes + +from typing import List, Any, Iterator +from .Config import UTF_8, kperf_so +from .Symbol import CtypesStack, Stack + + +class SampleRateUnion(ctypes.Union): + + _fields_ = [ + ('period', ctypes.c_uint), + ('freq', ctypes.c_uint) + ] + + +class CtypesPmuAttr(ctypes.Structure): + + _fields_ = [('evtList', ctypes.POINTER(ctypes.c_char_p)), + ('numEvt', ctypes.c_uint), + ('pidList', ctypes.POINTER(ctypes.c_int)), + ('numPid', ctypes.c_uint), + ('cpuList', ctypes.POINTER(ctypes.c_int)), + ('numCpu', ctypes.c_uint), + ('sampleRate', SampleRateUnion), + ('useFreq', ctypes.c_bool), + ('excludeUser', ctypes.c_bool), + ('excludeKernel', ctypes.c_bool), + ('symbolMode', ctypes.c_int), + ('dataFilter', ctypes.c_int), + ('evFilter', ctypes.c_int), + ('minLatency', ctypes.c_ulong)] + + def __init__(self, + evtList: List[str] = None, + pidList: List[int] = None, + cpuList: List[int] = None, + sampleRate: int = 0, + useFreq: bool = False, excludeUser: bool = False, excludeKernel: bool = False, + symbolMode: int = 0, dataFilter: int = 0, evFilter: int = 0, minLatency: int = 0, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + + if evtList is None: + self.evtList = None + self.numEvt = ctypes.c_uint(0) + else: + numEvt = len(evtList) + self.evtList = (ctypes.c_char_p * numEvt)(*[evt.encode(UTF_8) for evt in evtList]) + self.numEvt = ctypes.c_uint(numEvt) + + if pidList is None: + self.pidList = None + self.numPid = ctypes.c_uint(0) + else: + numPid = len(pidList) + self.pidList = (ctypes.c_int * numPid)(*pidList) + self.numPid = ctypes.c_uint(numPid) + + if cpuList is None: + self.cpuList = None + self.numCpu = ctypes.c_uint(0) + else: + numCpu = len(cpuList) + self.cpuList = (ctypes.c_int * numCpu)(*cpuList) + self.numCpu = ctypes.c_uint(numCpu) + if not useFreq: + self.sampleRate.period = ctypes.c_uint(sampleRate) + else: + self.sampleRate.freq = ctypes.c_uint(sampleRate) + + self.useFreq = ctypes.c_bool(useFreq) + self.excludeUser = ctypes.c_bool(excludeUser) + self.excludeKernel = ctypes.c_bool(excludeKernel) + + self.symbolMode = ctypes.c_int(symbolMode) + self.dataFilter = ctypes.c_int(dataFilter) + self.evFilter = ctypes.c_int(evFilter) + self.minLatency = ctypes.c_ulong(minLatency) + + + +class PmuAttr: + __slots__ = ['__c_pmu_attr'] + + def __init__(self, + evtList: List[str]=None, + pidList: List[int]=None, + cpuList: List[int]=None, + sampleRate: int=0, + useFreq: bool = False, excludeUser: bool = False, excludeKernel: bool = False, + symbolMode: int=0, dataFilter: int=0, evFilter: int=0, minLatency: int=0): + self.__c_pmu_attr = CtypesPmuAttr( + evtList=evtList, pidList=pidList, cpuList=cpuList, + sampleRate=sampleRate, + useFreq=useFreq, excludeUser=excludeUser, excludeKernel=excludeKernel, + symbolMode=symbolMode, dataFilter=dataFilter, evFilter=evFilter, minLatency=minLatency + ) + + @property + def c_pmu_attr(self) -> CtypesPmuAttr: + return self.__c_pmu_attr + + @property + def numEvt(self) -> int: + return self.c_pmu_attr.numEvt + + @property + def evtList(self) -> List[str]: + return [self.c_pmu_attr.evtList[i].decode(UTF_8) for i in range(self.numEvt)] + + @evtList.setter + def evtList(self, evtList: List[str]) -> None: + if evtList is None or not evtList: + self.c_pmu_attr.evtList = None + self.c_pmu_attr.numEvt = ctypes.c_uint(0) + else: + numEvt = len(evtList) + self.c_pmu_attr.evtList = (ctypes.c_char_p * numEvt)(*[evt.encode(UTF_8) for evt in evtList]) + self.c_pmu_attr.numEvt = ctypes.c_uint(numEvt) + + @property + def numPid(self) -> int: + return self.c_pmu_attr.numPid + + @property + def pidList(self) -> List[int]: + return [self.c_pmu_attr.pidList[i] for i in range(self.numPid)] + + @pidList.setter + def pidList(self, pidList: List[str]) -> None: + if pidList is None or not pidList: + self.c_pmu_attr.pidList = None + self.c_pmu_attr.numPid = ctypes.c_uint(0) + else: + numPid = len(pidList) + self.c_pmu_attr.pidList = (ctypes.c_char_p * numPid)(*[pid.encode(UTF_8) for pid in pidList]) + self.c_pmu_attr.numPid = ctypes.c_uint(numPid) + + @property + def numCpu(self) -> int: + return self.c_pmu_attr.numCpu + + @property + def cpuList(self) -> List[int]: + return [self.c_pmu_attr.cpuList[i] for i in range(self.numCpu)] + + @cpuList.setter + def cpuList(self, cpuList: List[str]) -> None: + if cpuList is None or not cpuList: + self.c_pmu_attr.cpuList = None + self.c_pmu_attr.numCpu = ctypes.c_uint(0) + else: + numCpu = len(cpuList) + self.c_pmu_attr.cpuList = (ctypes.c_char_p * numCpu)(*[cpu.encode(UTF_8) for cpu in cpuList]) + self.c_pmu_attr.numCpu = ctypes.c_uint(numCpu) + + + @property + def sampleRate(self) -> int: + if not self.useFreq: + return self.c_pmu_attr.sampleRate.period + else: + return self.c_pmu_attr.sampleRate.freq + + + @sampleRate.setter + def sampleRate(self, sampleRate) -> None: + if not self.useFreq: + self.c_pmu_attr.sampleRate.period = ctypes.c_uint(sampleRate) + else: + self.c_pmu_attr.sampleRate.freq = ctypes.c_uint(sampleRate) + + @property + def useFreq(self) -> bool: + return bool(self.c_pmu_attr.useFreq) + + @useFreq.setter + def useFreq(self, useFreq) -> None: + self.c_pmu_attr.useFreq = ctypes.c_bool(useFreq) + + @property + def excludeUser(self) -> bool: + return bool(self.c_pmu_attr.excludeUser) + + @excludeUser.setter + def excludeUser(self, excludeUser) -> None: + self.c_pmu_attr.excludeUser = ctypes.c_bool(excludeUser) + + @property + def excludeKernel(self) -> bool: + return bool(self.c_pmu_attr.excludeKernel) + + @excludeKernel.setter + def excludeKernel(self, excludeKernel) -> None: + self.c_pmu_attr.excludeKernel = ctypes.c_bool(excludeKernel) + + + @property + def symbolMode(self) -> int: + return self.c_pmu_attr.symbolMode + + @symbolMode.setter + def symbolMode(self, symbolMode) -> None: + self.c_pmu_attr.symbolMode = ctypes.c_int(symbolMode) + + @property + def dataFilter(self) -> int: + return self.c_pmu_attr.dataFilter + + @dataFilter.setter + def dataFilter(self, dataFilter) -> None: + self.c_pmu_attr.dataFilter = ctypes.c_int(dataFilter) + + @property + def evFilter(self) -> int: + return self.c_pmu_attr.evFilter + + @evFilter.setter + def evFilter(self, evFilter) -> None: + self.c_pmu_attr.evFilter = ctypes.c_int(evFilter) + + @property + def minLatency(self) -> int: + return self.c_pmu_attr.minLatency + + @minLatency.setter + def minLatency(self, minLatency) -> None: + self.c_pmu_attr.minLatency = ctypes.c_ulong(minLatency) + + @classmethod + def from_c_pmu_data(cls, c_pmu_attr: CtypesPmuAttr) -> 'PmuAttr': + pmu_attr = cls() + pmu_attr.__c_pmu_attr = c_pmu_attr + return pmu_attr + + +class CtypesCpuTopology(ctypes.Structure): + _fields_ = [ + ('coreId', ctypes.c_int), + ('numaId', ctypes.c_int), + ('socketId', ctypes.c_int) + ] + + def __init__(self, + coreId: int = 0, + numaId: int = 0, + socketId: int = 0, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + self.coreId = ctypes.c_int(coreId) + self.numaId = ctypes.c_int(numaId) + self.socketId = ctypes.c_int(socketId) + + +class CpuTopology: + __slots__ = ['__c_cpu_topo'] + + def __init__(self, + coreId: int = 0, + numaId: int = 0, + socketId: int = 0): + self.__c_cpu_topo = CtypesCpuTopology( + coreId=coreId, + numaId=numaId, + socketId=socketId + ) + + @property + def c_cpu_topo(self) -> CtypesCpuTopology: + return self.__c_cpu_topo + + @property + def coreId(self) -> int: + return self.c_cpu_topo.coreId + + @coreId.setter + def coreId(self, coreId) -> None: + self.c_cpu_topo.coreId = ctypes.c_int(coreId) + + @property + def numaId(self) -> int: + return self.c_cpu_topo.numaId + + @numaId.setter + def numaId(self, numaId) -> None: + self.c_cpu_topo.numaId = ctypes.c_int(numaId) + + @property + def socketId(self) -> int: + return self.c_cpu_topo.socketId + + @socketId.setter + def socketId(self, socketId) -> None: + self.c_cpu_topo.socketId = ctypes.c_int(socketId) + + + @classmethod + def from_c_cpu_topo(cls, c_cpu_topo: CtypesCpuTopology) -> 'CpuTopology': + cpu_topo = cls() + cpu_topo.__c_cpu_topo = c_cpu_topo + return cpu_topo + + + +class CtypesPmuDataExt(ctypes.Structure): + _fields_ = [ + ('pa', ctypes.c_ulong), + ('va', ctypes.c_ulong), + ('event', ctypes.c_ulong) + ] + + def __init__(self, + pa: int = 0, + va: int = 0, + event: int = 0, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + self.pa = ctypes.c_ulong(pa) + self.va = ctypes.c_ulong(va) + self.event = ctypes.c_ulong(event) + + +class PmuDataExt: + __slots__ = ['__c_pmu_data_ext'] + + def __init__(self, + pa: int = 0, + va: int = 0, + event: int = 0): + self.__c_pmu_data_ext = CtypesPmuDataExt( + pa=pa, + va=va, + event=event + ) + + @property + def c_pmu_data_ext(self) -> CtypesPmuDataExt: + return self.__c_pmu_data_ext + + @property + def pa(self) -> int: + return self.c_pmu_data_ext.pa + + @pa.setter + def pa(self, pa) -> None: + self.c_pmu_data_ext.pa = ctypes.c_int(pa) + + @property + def va(self) -> int: + return self.c_pmu_data_ext.va + + @va.setter + def va(self, va) -> None: + self.c_pmu_data_ext.va = ctypes.c_int(va) + + @property + def event(self) -> int: + return self.c_pmu_data_ext.event + + @event.setter + def event(self, event) -> None: + self.c_pmu_data_ext.event = ctypes.c_int(event) + + @classmethod + def from_pmu_data_ext(cls, c_pmu_data_ext: CtypesPmuDataExt) -> 'PmuDataExt': + pmu_data_ext = cls() + pmu_data_ext.__c_pmu_data_ext = c_pmu_data_ext + return pmu_data_ext + + +class CtypesPmuData(ctypes.Structure): + _fields_ = [ + ('stack', ctypes.POINTER(CtypesStack)), + ('evt', ctypes.c_char_p), + ('ts', ctypes.c_int64), + ('pid', ctypes.c_int), + ('tid', ctypes.c_int), + ('cpu', ctypes.c_uint), + ('cpuTopo', ctypes.POINTER(CtypesCpuTopology)), + ('comm', ctypes.c_char_p), + ('period', ctypes.c_int), + ('count', ctypes.c_uint64), + ('ext', ctypes.POINTER(CtypesPmuDataExt)) + ] + + def __init__(self, + stack: CtypesStack = None, + evt: str = '', + ts: int = 0, pid: int = 0, tid: int = 0, cpu: int = 0, + cpuTopo: CtypesCpuTopology = None, + comm: str = '', + period: int = 0, + count: int = 0, + ext: CtypesPmuDataExt = None, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + + self.stack = stack + self.evt = ctypes.c_char_p(evt.encode(UTF_8)) + self.ts = ctypes.c_int64(ts) + self.pid = ctypes.c_int(pid) + self.tid = ctypes.c_int(tid) + self.cpu = ctypes.c_uint(cpu) + self.cpuTopo = cpuTopo + self.comm = ctypes.c_char_p(comm.encode(UTF_8)) + self.period = ctypes.c_int(period) + self.count = ctypes.c_uint64(count) + self.ext = ext + + +class PmuData: + __slots__ = ['__c_pmu_data'] + + def __init__(self, + stack: Stack = None, + evt: str = '', + ts: int = 0, + pid: int = 0, + tid: int = 0, + cpu: int = 0, + cpuTopo: CpuTopology = None, + comm: str = '', + period: int = 0, + count: int = 0, + ext: PmuDataExt = None): + self.__c_pmu_data = CtypesPmuData( + stack=stack.c_stack if stack else None, + evt=evt, + ts=ts, + pid=pid, + tid=tid, + cpu=cpu, + cpuTopo=cpuTopo.c_cpu_topo if cpuTopo else None, + comm=comm, + period=period, + count=count, + ext=ext.c_pmu_data_ext if ext else None, + ) + + + @property + def c_pmu_data(self) -> CtypesPmuData: + return self.__c_pmu_data + + @property + def stack(self) -> Stack: + return Stack.from_c_stack(self.c_pmu_data.stack.contents) if self.c_pmu_data.stack else None + + @stack.setter + def stack(self, stack: Stack) -> None: + self.c_pmu_data.stack = stack.c_stack if stack else None + + @property + def evt(self) -> str: + return self.c_pmu_data.evt.decode(UTF_8) + + @evt.setter + def evt(self, evt) -> None: + self.c_pmu_data.evt = ctypes.c_char_p(evt.encode(UTF_8)) + + @property + def ts(self) -> int: + return self.c_pmu_data.ts + + @ts.setter + def ts(self, ts: int) -> None: + self.c_pmu_data.ts = ctypes.c_int64(ts) + + @property + def pid(self) -> int: + return self.c_pmu_data.pid + + @pid.setter + def pid(self, pid: int) -> None: + self.c_pmu_data.pid = ctypes.c_int(pid) + + @property + def tid(self) -> int: + return self.c_pmu_data.tid + + @tid.setter + def tid(self, tid: int) -> None: + self.c_pmu_data.tid = ctypes.c_int(tid) + + @property + def cpu(self) -> int: + return self.c_pmu_data.cpu + + @cpu.setter + def cpu(self, cpu: int) -> None: + self.c_pmu_data.cpu = ctypes.c_uint(cpu) + + @property + def cpuTopo(self) -> CpuTopology: + return CpuTopology.from_c_cpu_topo(self.c_pmu_data.cpuTopo.contents) if self.c_pmu_data.cpuTopo else None + + @cpuTopo.setter + def cpuTopo(self, cpuTopo: CpuTopology) -> None: + self.c_pmu_data.cpuTopo = cpuTopo.c_cpu_topo if cpuTopo else None + + @property + def comm(self) -> str: + return self.c_pmu_data.comm.decode(UTF_8) + + @comm.setter + def comm(self, comm) -> None: + self.c_pmu_data.comm = ctypes.c_char_p(comm.encode(UTF_8)) + + @property + def period(self) -> int: + return self.c_pmu_data.period + + @period.setter + def period(self, period) -> None: + self.c_pmu_data.period = ctypes.c_int(period) + + @property + def count(self) -> int: + return self.c_pmu_data.count + + @count.setter + def count(self, count) -> None: + self.c_pmu_data.count = ctypes.c_uint64(count) + + @property + def ext(self) -> PmuDataExt: + return PmuDataExt.from_pmu_data_ext(self.c_pmu_data.ext.contents) if self.c_pmu_data.ext else None + + @ext.setter + def ext(self, ext: PmuDataExt) -> None: + self.c_pmu_data.ext = ext.c_pmu_data_ext if ext else None + + @classmethod + def from_c_pmu_data(cls, c_pmu_data: CtypesPmuData) -> 'PmuData': + pmu_data = cls() + pmu_data.__c_pmu_data = c_pmu_data + return pmu_data + + +def PmuOpen(collectType: int, pmuAttr: PmuAttr) -> int: + c_PmuOpen = kperf_so.PmuOpen + c_PmuOpen.argtypes = [ctypes.c_int, ctypes.POINTER(CtypesPmuAttr)] + c_PmuOpen.restype = ctypes.c_int + + c_collectType = ctypes.c_int(collectType) + + return c_PmuOpen(c_collectType, ctypes.byref(pmuAttr.c_pmu_attr)) + + +def PmuEventListFree() -> None: + c_PmuEventListFree = kperf_so.PmuEventListFree + c_PmuEventListFree.argtypes = [] + c_PmuEventListFree.restype = None + + c_PmuEventListFree() + +def PmuEventList(eventType: int) -> Iterator[str]: + c_PmuEventList = kperf_so.PmuEventList + c_PmuEventList.argtypes = [ctypes.c_int] + c_PmuEventList.restype = ctypes.POINTER(ctypes.c_char_p) + + c_eventType = ctypes.c_int(eventType) + c_numEvt = ctypes.c_uint() + + eventList = c_PmuEventList(c_eventType, ctypes.byref(c_numEvt)) + # PmuEventListFree() + return (eventList[i].decode(UTF_8) for i in range(c_numEvt.value)) + + +def PmuEnable(pd: int) -> int: + c_PmuEnable = kperf_so.PmuEnable + c_PmuEnable.argtypes = [ctypes.c_int] + c_PmuEnable.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + + return c_PmuEnable(c_pd) + + +def PmuDisable(pd: int) -> int: + c_PmuDisable = kperf_so.PmuDisable + c_PmuDisable.argtypes = [ctypes.c_int] + c_PmuDisable.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + + return c_PmuDisable(c_pd) + + +def PmuCollect(pd: int, milliseconds: int, interval: int) -> int: + c_PmuCollect = kperf_so.PmuCollect + c_PmuCollect.argtypes = [ctypes.c_int, ctypes.c_int, ctypes.c_uint] + c_PmuCollect.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + c_milliseconds = ctypes.c_int(milliseconds) + c_interval = ctypes.c_uint(milliseconds) + + return c_PmuCollect(c_pd, c_milliseconds, c_interval) + +# def PmuCollectV(pd: ctypes.POINTER(ctypes.c_int), pd_len: ctypes.c_uint, milliseconds: ctypes.c_int) -> int: +# c_PmuCollectV = kperf_so.PmuCollectV +# c_PmuCollectV.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.c_uint, ctypes.c_int] +# c_PmuCollectV.restype = ctypes.c_int +# return c_PmuCollectV(pd, pd_len, milliseconds) +# + +def PmuStop(pd: int) -> None: + c_PmuStop = kperf_so.PmuStop + c_PmuStop.argtypes = [ctypes.c_int] + c_PmuStop.restype = None + + c_pd = ctypes.c_int(pd) + + c_PmuStop(c_pd) + + +def PmuDataFree(pmuData: ctypes.POINTER(CtypesPmuData)) -> None: + c_PmuDataFree = kperf_so.PmuDataFree + c_PmuDataFree.argtypes = [ctypes.POINTER(CtypesPmuData)] + c_PmuDataFree.restype = None + c_PmuDataFree(pmuData) + + +def PmuRead(pd: int) -> Iterator[PmuData]: + c_PmuRead = kperf_so.PmuRead + c_PmuRead.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.POINTER(CtypesPmuData))] + c_PmuRead.restype = ctypes.c_int + + c_pd = ctypes.c_int(pd) + c_data = ctypes.pointer(CtypesPmuData()) + + c_len_data = c_PmuRead(c_pd, ctypes.byref(c_data)) + data_iter = (PmuData.from_c_pmu_data(c_data[i]) for i in range(c_len_data)) + PmuDataFree(c_data) + return data_iter + + +def PmuAppendData(fromData: ctypes.POINTER(CtypesPmuData), toData: ctypes.POINTER(ctypes.POINTER(CtypesPmuData))) -> int: + c_PmuAppendData = kperf_so.PmuAppendData + c_PmuAppendData.argtypes = [ctypes.POINTER(CtypesPmuData), ctypes.POINTER(ctypes.POINTER(CtypesPmuData))] + c_PmuAppendData.restype = ctypes.c_int + + return c_PmuAppendData(fromData, toData) + + +def PmuClose(pd: int) -> None: + c_PmuClose = kperf_so.PmuClose + c_PmuClose.argtypes = [ctypes.c_int] + c_PmuClose.restype = None + + c_pd = ctypes.c_int(pd) + + c_PmuClose(c_pd) \ No newline at end of file diff --git a/python/modules/_libkperf/Symbol.py b/python/modules/_libkperf/Symbol.py new file mode 100644 index 0000000..8008284 --- /dev/null +++ b/python/modules/_libkperf/Symbol.py @@ -0,0 +1,405 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: ctype python Symbol module +""" +import ctypes +from typing import List, Any, Iterator +from .Config import UTF_8, sym_so + + +class CtypesSymbol(ctypes.Structure): + + _fields_ = [ + ('addr', ctypes.c_ulong), + ('module', ctypes.c_char_p), + ('symbolName', ctypes.c_char_p), + ('fileName', ctypes.c_char_p), + ('lineNum', ctypes.c_uint), + ('offset', ctypes.c_ulong), + ('codeMapEndAddr', ctypes.c_ulong), + ('codeMapAddr', ctypes.c_ulong), + ('count', ctypes.c_uint64) + ] + + def __init__(self, + addr: int = 0, + module: str = '', + symbolName: str = '', + fileName: str = '', + lineNum: int = 0, + offset: int = 0, + codeMapEndAddr: int = 0, + codeMapAddr: int = 0, + count: int = 0, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + self.addr = ctypes.c_ulong(addr) + self.module = ctypes.c_char_p(module.encode(UTF_8)) + + self.symbolName = ctypes.c_char_p(symbolName.encode(UTF_8)) + self.fileName = ctypes.c_char_p(fileName.encode(UTF_8)) + self.lineNum = ctypes.c_uint(lineNum) + self.offset = ctypes.c_ulong(offset) + + self.codeMapEndAddr = ctypes.c_ulong(codeMapEndAddr) + self.codeMapAddr = ctypes.c_ulong(codeMapAddr) + self.count = ctypes.c_uint64(count) + + +class Symbol: + + __slots__ = ['__c_sym'] + + def __init__(self, + addr: int = 0, + module: str = '', + symbolName: str = '', + fileName: str = '', + lineNum: int = 0, + offset: int = 0, + codeMapEndAddr: int = 0, + codeMapAddr: int = 0, + count: int = 0): + self.__c_sym = CtypesSymbol( + addr=addr, module=module, + symbolName=symbolName, fileName=fileName, lineNum=lineNum, offset=offset, + codeMapEndAddr=codeMapEndAddr, codeMapAddr=codeMapAddr, + count=count + ) + + @property + def c_sym(self) -> CtypesSymbol: + return self.__c_sym + + @property + def addr(self) -> int: + return self.c_sym.addr + + @addr.setter + def addr(self, addr: int) -> None: + self.c_sym.addr = ctypes.c_ulong(addr) + + @property + def module(self) -> str: + return self.c_sym.module.decode(UTF_8) + + @module.setter + def module(self, module: str) -> None: + self.c_sym.module = ctypes.c_char_p(module.encode(UTF_8)) + + @property + def symbolName(self) -> str: + return self.c_sym.symbolName.decode(UTF_8) + + @symbolName.setter + def symbolName(self, symbolName: str) -> None: + self.c_sym.symbolName = ctypes.c_char_p(symbolName.encode(UTF_8)) + + @property + def fileName(self) -> str: + return self.c_sym.fileName.decode(UTF_8) + + @fileName.setter + def fileName(self, fileName: str) -> None: + self.c_sym.fileName = ctypes.c_char_p(fileName.encode(UTF_8)) + + @property + def lineNum(self) -> int: + return self.c_sym.lineNum + + @lineNum.setter + def lineNum(self, lineNum: int) -> None: + self.c_sym.lineNum = ctypes.c_uint(lineNum) + + @property + def offset(self) -> int: + return self.c_sym.offset + + @offset.setter + def offset(self, offset: int) -> None: + self.c_sym.offset = ctypes.c_ulong(offset) + + @property + def codeMapEndAddr(self) -> int: + return self.c_sym.codeMapEndAddr + + @codeMapEndAddr.setter + def codeMapEndAddr(self, codeMapEndAddr: int) -> None: + self.c_sym.codeMapEndAddr = ctypes.c_ulong(codeMapEndAddr) + + @property + def codeMapAddr(self) -> int: + return self.c_sym.codeMapAddr + + @codeMapAddr.setter + def codeMapAddr(self, codeMapAddr: int) -> None: + self.c_sym.codeMapAddr = ctypes.c_ulong(codeMapAddr) + + @property + def count(self) -> int: + return self.c_sym.count + + @count.setter + def count(self, count: int) -> None: + self.c_sym.count = ctypes.c_uint64(count) + + @classmethod + def from_c_sym(cls, c_sym: CtypesSymbol) -> 'Symbol': + symbol = cls() + symbol.__c_sym = c_sym + return symbol + + +class CtypesStack(ctypes.Structure): + + _fields_ = [ + ('symbol', ctypes.POINTER(CtypesSymbol)), + ('next', ctypes.POINTER('CtypesStack')), + ('prev', ctypes.POINTER('CtypesStack')), + ('count', ctypes.c_uint64) + ] + + def __init__(self, + symbol: CtypesSymbol = None, + next: 'CtypesStack' = None, + prev: 'CtypesStack' = None, + count: int = 0, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + """ + ctypes中,一个ctypes对象赋值给一个ctypes.POINTER类型的字段时 + ctypes会自动创建一个指向该对象的指针,不需要显式地调用ctypes.byref()或ctypes.pointer()来获取对象的引用 + """ + self.symbol = symbol + self.next = next + self.prev = prev + self.count = ctypes.c_uint64(count) + + +class Stack: + + __slots__ = ['__c_stack'] + + def __init__(self, + symbol: Symbol = None, + next: 'Stack' = None, + prev: 'Stack' = None, + count: int = 0): + self.__c_stack = CtypesStack( + symbol=symbol.c_sym if symbol else None, + next=next.c_stack if next else None, + prev=prev.c_stack if next else None, + count=count + ) + + @property + def c_stack(self) -> CtypesStack: + return self.__c_stack + + @property + def symbol(self) -> Symbol: + return Symbol.from_c_sym(self.c_stack.symbol.contents) if self.c_stack.symbol else None + + @symbol.setter + def symbol(self, symbol: Symbol) -> None: + self.c_stack.symbol = symbol.c_sym if symbol else None + + + @property + def next(self) -> 'Stack': + return self.from_c_stack(self.c_stack.next.contents) if self.c_stack.next else None + + @next.setter + def next(self, next: 'Stack') -> None: + self.c_stack.next = next.c_stack if next else None + + + @property + def prev(self) -> 'Stack': + return self.from_c_stack(self.c_stack.prev.contents) if self.c_stack.prev else None + + @prev.setter + def prev(self, prev: 'Stack') -> None: + self.c_stack.prev = prev.c_stack if prev else None + + @property + def count(self) -> int: + return self.c_stack.count + + @count.setter + def count(self, count) -> None: + self.c_stack.count = ctypes.c_uint64(count) + + @classmethod + def from_c_stack(cls, c_stack: CtypesStack) -> 'Stack': + stack = cls() + stack.__c_stack = c_stack + return stack + + +class CtypesAsmCode(ctypes.Structure): + + _fields_ = [ + ('addr', ctypes.c_ulong), + ('code', ctypes.c_char_p), + ('fileName', ctypes.c_char_p), + ('lineNum', ctypes.c_uint) + ] + + def __init__(self, + addr: int = 0, + code: str = '', + fileName: str = '', + lineNum: int = 0, + *args: Any, **kw: Any): + super().__init__(*args, **kw) + self.addr = ctypes.c_ulong(addr) + self.code = ctypes.c_char_p(code.encode(UTF_8)) + self.fileName = ctypes.c_char_p(fileName.encode(UTF_8)) + self.lineNum = ctypes.c_uint(lineNum) + + +class AsmCode: + + __slots__ = ['__c_asm_code'] + + def __init__(self, + addr: int = 0, + code: str = '', + fileName: str = '', + lineNum: int = 0): + self.__c_asm_code = CtypesAsmCode( + addr=addr, + code=code, + fileName=fileName, + lineNum=lineNum + ) + + @property + def c_asm_code(self) -> CtypesAsmCode: + return self.__c_asm_code + + @property + def addr(self) -> int: + return self.c_asm_code.addr + + @addr.setter + def addr(self, addr) -> None: + self.c_asm_code.addr = ctypes.c_ulong(addr) + + @property + def code(self) -> str: + return self.c_asm_code.code.decode(UTF_8) + + @code.setter + def code(self, code: str) -> None: + self.c_asm_code.code = ctypes.c_char_p(code.encode(UTF_8)) + + @property + def fileName(self) -> str: + return self.c_asm_code.fileName.decode(UTF_8) + + @fileName.setter + def fileName(self, fileName: str) -> None: + self.c_asm_code.fileName = ctypes.c_char_p(fileName.encode(UTF_8)) + + @property + def lineNum(self) -> int: + return self.c_asm_code.lineNum + + @lineNum.setter + def lineNum(self, lineNum) -> None: + self.c_asm_code.lineNum = ctypes.c_uint(lineNum) + + @classmethod + def from_c_asm_code(cls, c_asm_code: CtypesAsmCode) -> 'AsmCode': + asm_code = cls() + asm_code.__c_asm_code = c_asm_code + return asm_code + + +def SymResolverRecordKernel() -> None: + c_SymResolverRecordKernel = sym_so.SymResolverRecordKernel + c_SymResolverRecordKernel.argtypes = [] + c_SymResolverRecordKernel.restype = ctypes.c_int + + c_SymResolverRecordKernel() + + +def SymResolverRecordModule(pid: int) -> None: + c_SymResolverRecordModule = sym_so.SymResolverRecordModule + c_SymResolverRecordModule.argtypes = [ctypes.c_int] + c_SymResolverRecordModule.restype = ctypes.c_int + + c_pid = ctypes.c_int(pid) + + c_SymResolverRecordModule(c_pid) + + +def SymResolverRecordModuleNoDwarf(pid: int) -> None: + c_SymResolverRecordModuleNoDwarf = sym_so.SymResolverRecordModuleNoDwarf + c_SymResolverRecordModuleNoDwarf.argtypes = [ctypes.c_int] + c_SymResolverRecordModuleNoDwarf.restype = ctypes.c_int + + c_pid = ctypes.c_int(pid) + + c_SymResolverRecordModuleNoDwarf(c_pid) + + +def StackToHash(pid: int, stackList: List[int]) -> Iterator[Stack]: + c_StackToHash = sym_so.StackToHash + c_StackToHash.argtypes = [ctypes.c_int, ctypes.POINTER(ctypes.c_ulong), ctypes.c_int] + c_StackToHash.restype = ctypes.POINTER(CtypesStack) + + stack_len = len(stackList) + c_pid = ctypes.c_int(pid) + c_stack_list = (ctypes.c_ulong * stack_len)(*stackList) + c_nr = ctypes.c_int(stack_len) + + c_stack = c_StackToHash(c_pid, c_stack_list, c_nr) + while c_stack: + # 此处指针转换可能还有问题,TODO + stack = Stack.from_c_stack(c_stack) + yield stack + c_stack = c_stack.contents.next + + +def SymResolverMapAddr(pid: int, addr: int) -> Symbol: + c_SymResolverMapAddr = sym_so.SymResolverMapAddr + c_SymResolverMapAddr.argtypes = [ctypes.c_int, ctypes.c_ulong] + c_SymResolverMapAddr.restype = ctypes.POINTER(CtypesSymbol) + + c_pid = ctypes.c_int(pid) + c_addr = ctypes.c_ulong(addr) + + c_sym = c_SymResolverMapAddr(c_pid, c_addr) + # 此处指针转换可能还有问题,TODO + return Symbol.from_c_sym(c_sym) + + +def FreeModuleData(pid: int) -> None: + c_FreeModuleData = sym_so.FreeModuleData + c_FreeModuleData.argtypes = [ctypes.c_int] + c_FreeModuleData.restype = None + + c_pid = ctypes.c_int(pid) + + c_FreeModuleData(c_pid) + + +def SymResolverDestroy() -> None: + c_SymResolverDestroy = sym_so.SymResolverDestroy + c_SymResolverDestroy.argtypes = [] + c_SymResolverDestroy.restype = None + + c_SymResolverDestroy() diff --git a/python/modules/_libkperf/__init__.py b/python/modules/_libkperf/__init__.py new file mode 100644 index 0000000..30d5c56 --- /dev/null +++ b/python/modules/_libkperf/__init__.py @@ -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: Victor Jin +Create: 2024-05-10 +Description: ctype python _libkperf module +""" +__copyright__ = 'Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.' +__license__ = 'MulanPSL2' + +from .Config import * +from .Symbol import * +from .Pmu import * +from .Perror import * + +# From Config +__version__ = VERSION diff --git a/python/modules/kperf/__init__.py b/python/modules/kperf/__init__.py new file mode 100644 index 0000000..64fd17d --- /dev/null +++ b/python/modules/kperf/__init__.py @@ -0,0 +1,16 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: kperf module +""" +from .pmu import * +from .perror import * diff --git a/python/modules/kperf/perror.py b/python/modules/kperf/perror.py new file mode 100644 index 0000000..b293c16 --- /dev/null +++ b/python/modules/kperf/perror.py @@ -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: Victor Jin +Create: 2024-05-16 +Description: kperf perror module +""" +import _libkperf + + +def errorno() -> int: + return _libkperf.Perrorno() + + +def error()-> str: + return _libkperf.Perror() + + +__all__ = [ + 'errorno', + 'error', +] diff --git a/python/modules/kperf/pmu.py b/python/modules/kperf/pmu.py new file mode 100644 index 0000000..ecfb621 --- /dev/null +++ b/python/modules/kperf/pmu.py @@ -0,0 +1,127 @@ +""" +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: Victor Jin +Create: 2024-05-16 +Description: kperf pmu module +""" +from typing import Iterator + +import _libkperf + + +class PmuTaskType: + COUNTING = 0 + SAMPLING = 1 + SPE_SAMPLING = 2 + MAX_TASK_TYPE = 3 + + +class PmuEventType: + CORE_EVENT = 0 + UNCORE_EVENT = 1 + TRACE_EVENT = 2 + ALL_EVENT = 3 + + +class SpeFilter: + SPE_FILTER_NONE = 0 + TS_ENABLE = 1 << 0 # enable timestamping with value of generic timer + PA_ENABLE = 1 << 1 # collect physical address (as well as VA) of loads/stores + PCT_ENABLE = 1 << 2 # collect physical timestamp instead of virtual timestamp + JITTER = 1 << 16 # use jitter to avoid resonance when sampling + BRANCH_FILTER = 1 << 32 # collect branches only + LOAD_FILTER = 1 << 33 # collect loads only + STORE_FILTER = 1 << 34 # collect stores only + SPE_DATA_ALL = TS_ENABLE | PA_ENABLE | PCT_ENABLE | JITTER | BRANCH_FILTER | LOAD_FILTER | STORE_FILTER + + +class 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 + + +class SpeEvent: + SPE_EV_EXCEPT = 1 << 0 + SPE_EV_RETIRED = 1 << 1 + SPE_EV_L1D_ACCESS = 1 << 2 + SPE_EV_L1D_REFILL = 1 << 3 + SPE_EV_TLB_ACCESS = 1 << 4 + SPE_EV_TLB_WALK = 1 << 5 + SPE_EV_NOT_TAKEN = 1 << 6 + SPE_EV_MISPRED = 1 << 7 + SPE_EV_LLC_ACCESS = 1 << 8 + SPE_EV_LLC_MISS = 1 << 9 + SPE_EV_REMOTE_ACCESS= 1 << 10 + SPE_EV_ALIGNMENT = 1 << 11 + SPE_EV_PARTIAL_PRED = 1 << 17 + SPE_EV_EMPTY_PRED = 1 << 18 + + +class SymbolMode: + NO_SYMBOL_RESOLVE = 0 # in PmuData will be set to NULL. + RESOLVE_ELF = 1 # Resolve elf only. Fields except lineNum and fileName in Symbol will be valid. + RESOLVE_ELF_DWARF = 2 # Resolve elf and dwarf. All fields in Symbol will be valid. + + +class PmuAttr(_libkperf.PmuAttr): + pass + +class PmuData(_libkperf.PmuData): + pass + + +def open(collect_type: PmuTaskType, pmu_attr: PmuAttr) -> int: + return _libkperf.PmuOpen(int(collect_type), pmu_attr) + + +def event_list(event_type: PmuEventType)-> Iterator[str]: + return _libkperf.PmuEventList(int(event_type)) + + +def enable(pd: int)-> int: + return _libkperf.PmuEnable(pd) + + +def disable(pd: int)-> int: + return _libkperf.PmuDisable(pd) + + +def read(pd: int) -> Iterator[PmuData]: + return _libkperf.PmuRead(pd) + + +def stop(pd: int) -> None: + return _libkperf.PmuStop(pd) + + +def close(pd: int) -> None: + return _libkperf.PmuClose(pd) + + +__all__ = [ + 'PmuTaskType', + 'PmuEventType', + 'SpeFilter', + 'SpeEventFilter', + 'SymbolMode', + 'PmuAttr', + 'PmuData', + 'open', + 'event_list', + 'enable', + 'disable', + 'read', + 'stop', + 'close', +] \ No newline at end of file diff --git a/python/modules/ksym/__init__.py b/python/modules/ksym/__init__.py new file mode 100644 index 0000000..e6a541f --- /dev/null +++ b/python/modules/ksym/__init__.py @@ -0,0 +1,15 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: ksym module +""" +from .symbol import * \ No newline at end of file diff --git a/python/modules/ksym/symbol.py b/python/modules/ksym/symbol.py new file mode 100644 index 0000000..c02f16d --- /dev/null +++ b/python/modules/ksym/symbol.py @@ -0,0 +1,59 @@ +""" +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: Victor Jin +Create: 2024-05-10 +Description: ksym symbol module +""" +from typing import List, Iterator + +import _libkperf + + +class Stack(_libkperf.Stack): + pass + + +class Symbol(_libkperf.Symbol): + pass + + +def record_kernel() -> None: + _libkperf.SymResolverRecordKernel() + + +def record_module(pid: int, dwarf: bool = True) -> None: + if dwarf: + _libkperf.SymResolverRecordModule(pid) + else: + _libkperf.SymResolverRecordModuleNoDwarf(pid) + + +def get_stack(pid: int, stacks: List[int]) -> Iterator[Stack]: + return _libkperf.StackToHash(pid, stacks) + + +def get_symbol(pid: int, addr: int) -> Symbol: + return _libkperf.SymResolverMapAddr(pid, addr) + + +def free_module(pid: int) -> None: + _libkperf.FreeModuleData(pid) + + +__all__ = [ + 'Stack', + 'Symbol', + 'record_kernel', + 'record_module', + 'get_stack', + 'get_symbol', + 'free_module', +] \ No newline at end of file diff --git a/python/modules/setup.py.in b/python/modules/setup.py.in new file mode 100644 index 0000000..0785ba0 --- /dev/null +++ b/python/modules/setup.py.in @@ -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: Victor Jin +Create: 2024-05-10 +Description: libkperf python module setup ini +""" +from setuptools import setup, find_packages + +libkperf_path = '@LIBKPERF_PATH@' +libsym_path = '@LIBSYM_PATH@' + +setup( + name='libkperf', + version='1.0', + packages=find_packages(), + data_files=[('_libkperf', [libkperf_path, libsym_path])] +) + -- Gitee From 57483aa47d474744ce51ff4454fbe6896a2ec580 Mon Sep 17 00:00:00 2001 From: victorjin Date: Thu, 16 May 2024 20:26:57 +0800 Subject: [PATCH 2/6] add libkperf python module --- README.en.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.en.md b/README.en.md index 4b63a2f..b139f6b 100644 --- a/README.en.md +++ b/README.en.md @@ -22,7 +22,7 @@ As mentioned above, the header and library will be installed in the/home/libkper If you want to add additional python library support, you can install it as follows: ```shell -bash build.sh python +bash build.sh python=true ``` If you need to uninstall the python library after installation, you can run the following command: -- Gitee From e299fb24abd4973b4ed5f25f71cccc15e2709b75 Mon Sep 17 00:00:00 2001 From: victorjin Date: Thu, 16 May 2024 20:28:59 +0800 Subject: [PATCH 3/6] add libkperf python module --- CMakeLists.txt | 1 - 1 file changed, 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index dec775a..bca0eb9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,7 +40,6 @@ include(${CMAKE_CURRENT_LIST_DIR}/Common.cmake) add_subdirectory(symbol) add_subdirectory(pmu) -option(PYTHON "Build python module" OFF) if (PYTHON) add_subdirectory(python) endif() -- Gitee From 16df40e425dc7b42dbea08b911473b7ece285db4 Mon Sep 17 00:00:00 2001 From: victorjin Date: Thu, 16 May 2024 20:49:12 +0800 Subject: [PATCH 4/6] add libkperf python module --- python/modules/_libkperf/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/modules/_libkperf/__init__.py b/python/modules/_libkperf/__init__.py index 30d5c56..2241467 100644 --- a/python/modules/_libkperf/__init__.py +++ b/python/modules/_libkperf/__init__.py @@ -13,7 +13,7 @@ Create: 2024-05-10 Description: ctype python _libkperf module """ __copyright__ = 'Copyright (c) Huawei Technologies Co., Ltd. 2024. All rights reserved.' -__license__ = 'MulanPSL2' +__license__ = 'Mulan PSL v2' from .Config import * from .Symbol import * -- Gitee From dab75e7083b5bbd88a2ea4925c025cbe167ff90c Mon Sep 17 00:00:00 2001 From: victorjin Date: Thu, 16 May 2024 21:01:44 +0800 Subject: [PATCH 5/6] add libkperf python module --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba61bd2..99ee66e 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ bash build.sh installPath=/home/libkperf 如果要额外增加python库支持,可以通过如下方式安装 ```shell -bash build.sh PYTHON=true +bash build.sh python=true ``` 安装后若需要卸载python库, 可以执行下述命令 -- Gitee From d2e6f986702d72df010409f2d2d8a722bfa10fe6 Mon Sep 17 00:00:00 2001 From: victorjin Date: Fri, 17 May 2024 10:21:49 +0800 Subject: [PATCH 6/6] add libkperf python module --- python/modules/_libkperf/Config.py | 10 +++++++++- python/modules/_libkperf/Perror.py | 6 ++++++ python/modules/_libkperf/Pmu.py | 23 +++++++++++++++-------- python/modules/_libkperf/Symbol.py | 15 +++++++++++++++ 4 files changed, 45 insertions(+), 9 deletions(-) diff --git a/python/modules/_libkperf/Config.py b/python/modules/_libkperf/Config.py index 1468fe9..a34945c 100644 --- a/python/modules/_libkperf/Config.py +++ b/python/modules/_libkperf/Config.py @@ -39,4 +39,12 @@ def libkperf_path(): sym_so = ctypes.CDLL(libsym_path()) -kperf_so = ctypes.CDLL(find_library(libkperf_path())) \ No newline at end of file +kperf_so = ctypes.CDLL(find_library(libkperf_path())) + + +__all__ = [ + 'UTF_8', + 'VERSION', + 'sym_so', + 'kperf_so', +] diff --git a/python/modules/_libkperf/Perror.py b/python/modules/_libkperf/Perror.py index bc442c1..cbdfc0b 100644 --- a/python/modules/_libkperf/Perror.py +++ b/python/modules/_libkperf/Perror.py @@ -29,3 +29,9 @@ def Perror() -> str: c_Perror.argtypes = [] c_Perror.restype = ctypes.c_char_p return c_Perror().decode(UTF_8) + + +__all__ = [ + 'Perrorno', + 'Perror', +] diff --git a/python/modules/_libkperf/Pmu.py b/python/modules/_libkperf/Pmu.py index 00dc466..e3f2f1e 100644 --- a/python/modules/_libkperf/Pmu.py +++ b/python/modules/_libkperf/Pmu.py @@ -568,6 +568,7 @@ def PmuEventListFree() -> None: c_PmuEventListFree() + def PmuEventList(eventType: int) -> Iterator[str]: c_PmuEventList = kperf_so.PmuEventList c_PmuEventList.argtypes = [ctypes.c_int] @@ -577,7 +578,6 @@ def PmuEventList(eventType: int) -> Iterator[str]: c_numEvt = ctypes.c_uint() eventList = c_PmuEventList(c_eventType, ctypes.byref(c_numEvt)) - # PmuEventListFree() return (eventList[i].decode(UTF_8) for i in range(c_numEvt.value)) @@ -612,12 +612,6 @@ def PmuCollect(pd: int, milliseconds: int, interval: int) -> int: return c_PmuCollect(c_pd, c_milliseconds, c_interval) -# def PmuCollectV(pd: ctypes.POINTER(ctypes.c_int), pd_len: ctypes.c_uint, milliseconds: ctypes.c_int) -> int: -# c_PmuCollectV = kperf_so.PmuCollectV -# c_PmuCollectV.argtypes = [ctypes.POINTER(ctypes.c_int), ctypes.c_uint, ctypes.c_int] -# c_PmuCollectV.restype = ctypes.c_int -# return c_PmuCollectV(pd, pd_len, milliseconds) -# def PmuStop(pd: int) -> None: c_PmuStop = kperf_so.PmuStop @@ -665,4 +659,17 @@ def PmuClose(pd: int) -> None: c_pd = ctypes.c_int(pd) - c_PmuClose(c_pd) \ No newline at end of file + c_PmuClose(c_pd) + + +__all__ = [ + 'PmuAttr', + 'PmuData', + 'PmuOpen', + 'PmuEventList', + 'PmuEnable', + 'PmuDisable', + 'PmuStop', + 'PmuRead', + 'PmuClose', +] diff --git a/python/modules/_libkperf/Symbol.py b/python/modules/_libkperf/Symbol.py index 8008284..d277ea3 100644 --- a/python/modules/_libkperf/Symbol.py +++ b/python/modules/_libkperf/Symbol.py @@ -403,3 +403,18 @@ def SymResolverDestroy() -> None: c_SymResolverDestroy.restype = None c_SymResolverDestroy() + + +__all__ = [ + 'CtypesStack', + 'Stack', + 'CtypesSymbol', + 'Symbol', + 'SymResolverRecordKernel', + 'SymResolverRecordModule', + 'SymResolverRecordModuleNoDwarf', + 'StackToHash', + 'SymResolverMapAddr', + 'FreeModuleData', + 'SymResolverDestroy', +] -- Gitee