libkperf是一个轻量级linux性能采集库,它能够让开发者以API的方式执行性能采集,包括pmu采样和符号解析。libkperf把采集数据内存化,使开发者能够在内存中直接处理采集数据,避免了读写perf.data带来的开销。
- 想要轻量化采集系统和应用的性能数据。
- 当分析一个高负载的应用,却不想对应用性能有较大影响的时候。
- 不想管理多个性能分析进程(perf),想获取易于解析的性能数据。
- 想轻量化地获取cpu的热点。
- 想分析cache的miss分布和时延。
- 想要跟踪系统调用函数的耗时。
- 想获取内存读写带宽。
- 想获取网络带宽和时延。
- 鲲鹏
- openEuler
- OpenCloudOS
- TencentOS
- KylinOS
- CentOS
v1.3:
- 支持对系统调用函数的耗时采集。
- 支持历史分支记录的采集(BRBE)。
v1.2:
- 支持tracepoint的采集和跟踪。
- 支持事件分组采集。
v1.1:
- 提供python的API。
v1.0:
- 支持采集pmu计数、采样和SPE采样功能。
- 支持core和uncore事件的采集。
- 支持符号解析功能。
最低依赖gcc版本:
- gcc-4.8.5 和 glibc-2.17
最低依赖python版本:
- python-3.7
编译生成动态库和C的API:
git clone --recurse-submodules https://gitee.com/openeuler/libkperf.git
cd libkperf
bash build.sh install_path=/path/to/install
说明:
- 如果编译报错提示没有numa.h文件,需要先安装对应的numactl-devel包。
- 如果编译连接在Found PythonInterp报CMake错误,需要先安装所需的python3-devel包。
如果想要编译调试版本:
bash build.sh install_path=/path/to/install buildType=debug
如果想要编译python包:
bash build.sh install_path=/path/to/install python=true
安装后若需要卸载python库, 可以执行下述命令
python3 -m pip uninstall -y libkperf
想要编译go的包
bash build.sh go=true
执行成功后,需要把go/src/libkperf整个目录拷贝到 $GOPATH/src/下,$GOPATH为用户项目目录
详细文档可以参考docs目录:
Python API文档可以参考docs目录:
Go API文档可以参考GO_API.md:
主要有以下几个API:
- PmuOpen 输入pid、core id和event,打开pmu设备。
- PmuEnable 开始收集。
- PmuRead 读取采集数据。
- PmuDisable 停止收集。
- PmuClose 关闭pmu设备。
以下是一些示例:
- 获取进程的pmu计数。
int pidList[1];
pidList[0] = pid;
char *evtList[1];
evtList[0] = "cycles";
// 初始化事件列表,指定需要计数的事件cycles。
PmuAttr attr = {0};
attr.evtList = evtList;
attr.numEvt = 1;
attr.pidList = pidList;
attr.numPid = 1;
// 调用PmuOpen,返回pd。pd表示该任务的id。
int pd = PmuOpen(COUNTING, &attr);
// 开始采集。
PmuEnable(pd);
// 采集1秒。
sleep(1);
// 停止采集。
PmuDisable(pd);
PmuData *data = NULL;
// 读取PmuData,它是一个数组,长度是len。
int len = PmuRead(pd, &data);
for (int i = 0; i < len; ++i) {
PmuData *d = &data[i];
...
}
// 释放PmuData。
PmuDataFree(data);
// 类似fd,当任务结束时调用PmuClose释放资源。
PmuClose(pd);
- 对进程进行采样
int pidList[1];
pidList[0] = pid;
char *evtList[1];
evtList[0] = "cycles";
// 初始化事件列表,指定需要计数的事件cycles。
PmuAttr attr = {0};
attr.evtList = evtList;
attr.numEvt = 1;
attr.pidList = pidList;
attr.numPid = 1;
// 调用PmuOpen,返回pd。pd表示该任务的id。
int pd = PmuOpen(SAMPLING, &attr);
// 开始采集。
PmuEnable(pd);
// 采集1秒。
sleep(1);
// 停止采集。
PmuDisable(pd);
PmuData *data = NULL;
// 读取PmuData,它是一个数组,长度是len。
int len = PmuRead(pd, &data);
for (int i = 0; i < len; ++i) {
// 获取数组的一个元素。
PmuData *d = &data[i];
// 获取调用栈对象,它是一个链表。
Stack *stack = d->stack;
while (stack) {
// 获取符号对象。
if (stack->symbol) {
...
}
stack = stack->next;
}
}
// 释放PmuData。
PmuDataFree(data);
// 类似fd,当任务结束时调用PmuClose释放资源。
PmuClose(pd);
Python 例子:
import time
from collections import defaultdict
import subprocess
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.iter:
evtMap[data.evt] += data.count
for evt, count in evtMap.items():
print(f"event: {evt} count: {count}")
kperf.disable(pd)
kperf.close(pd)
Go 例子
import "libkperf/kperf"
import "fmt"
import "time"
func main() {
attr := kperf.PmuAttr{EvtList:[]string{"cycles"}, SymbolMode:kperf.ELF}
fd, err := kperf.PmuOpen(kperf.COUNT, attr)
if err != nil {
fmt.Printf("kperf pmuopen counting failed, expect err is nil, but is %v\n", err)
return
}
kperf.PmuEnable(fd)
time.Sleep(time.Second)
kperf.PmuDisable(fd)
dataVo, err := kperf.PmuRead(fd)
if err != nil {
fmt.Printf("kperf pmuread failed, expect err is nil, but is %v\n", err)
return
}
for _, o := range dataVo.GoData {
fmt.Printf("event: %v count: %v", o.Evt, o.Count)
}
kperf.PmuDataFree(dataVo)
kperf.PmuClose(fd)
}
- 针对C++示例代码: 可以将示例代码放到一个c++源文件的main函数中,并引用此动态库相关的头文件(#include "symbol.h"、#include "pmu.h"、#include "pcerrc.h"),再使用g++编译链接此动态库,生成可执行文件即可运行。
编译指令参考:
g++ -o example example.cpp -I /install_path/include -L /install_path/lib -lkperf -lsym
- 针对python示例代码: 可以将示例代码放到一个python源文件的main函数中,并导入此动态库相关的头文件包(import kperf、import ksym),在运行此python文件即可。
运行指令参考:
python example.py
- 针对Go示例代码: 可以直接跳转到 go/src/libkperf/libkperf_test目录下
go test -v # 全部运行
go test -v -test.run TestCount #指定运行的用例