Linux内核 eBPF基础:perfperf_event_open系统调用与用户手册详解

Posted rtoax

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux内核 eBPF基础:perfperf_event_open系统调用与用户手册详解相关的知识,希望对你有一定的参考价值。

Linux内核 eBPF基础
perf(4)perf_event_open系统调用与用户手册详解


荣涛
2021年5月19日

在上篇讲到使用strace最终perf stat ls指令的系统调用,perf先从proc文件系统中获取内核支持的perf event,然后使用系统调用perf_event_open和ioctl控制内核中perf_event的使能,并从文件描述符中读取数据。本文将对系统调用perf_event_open进行讲解。

1. perf_event_open系统调用

1.1. 函数原型

#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>

int perf_event_open(struct perf_event_attr *attr,
                   pid_t pid, int cpu, int group_fd,
                   unsigned long flags);

它在lib中是没有wrapper的,所以可以自己定义:

static int perf_event_open(struct perf_event_attr *evt_attr, pid_t pid,
                                int cpu, int group_fd, unsigned long flags)
{
    int ret;
    ret = syscall(__NR_perf_event_open, evt_attr, pid, cpu, group_fd, flags);
    return ret;
}

1.1.1. pid

参数pid允许事件以各种方式附加到进程。

  • 如果pid为0,则在当前线程上进行测量;
  • 如果pid大于0,则对pid指示的进程进行测量;
  • 如果pid为-1,则对所有进程进行计数。

1.1.2. cpu

cpu参数允许测量特定于CPU。

  • 如果cpu>=0,则将测量限制为指定的CPU;否则,将限制为0。
  • 如果cpu=-1,则在所有CPU上测量事件。

请注意,pid == -1cpu == -1的组合无效。

  • pid> 0cpu == -1设置会测量每个进程,并将该进程跟随该进程计划调度到的任何CPU。 每个用户都可以创建每个进程的事件。
  • 每个CPU的pid == -1cpu>= 0设置是针对每个CPU的,并测量指定CPU上的所有进程。 每CPU事件需要CAP_SYS_ADMIN功能或小于/ 1的/proc/sys/kernel/perf_event_paranoid值。见《perf_event相关的配置文件》章节。

1.1.3. group_fd

group_fd参数允许创建事件组。 一个事件组有一个事件,即组长。 首先创建领导者,group_fd = -1。 其余的组成员是通过随后的perf_event_open()调用创建的,其中group_fd设置为组长的fd。 (使用group_fd = -1单独创建一个事件,并且该事件被认为是只有1个成员的组。)将事件组作为一个单元调度到CPU:仅当所有 可以将组中的事件放到CPU上。 这意味着成员事件的值可以彼此有意义地进行比较,相加,除法(以获得比率)等,因为它们已经为同一组已执行指令计数了事件。

1.1.4. flags

#define PERF_FLAG_FD_NO_GROUP		(1UL << 0)
#define PERF_FLAG_FD_OUTPUT		(1UL << 1)
#define PERF_FLAG_PID_CGROUP		(1UL << 2) /* pid=cgroup id, per-cpu mode only */
#define PERF_FLAG_FD_CLOEXEC		(1UL << 3) /* O_CLOEXEC */

在系统调用man手册中是这样讲解的:

  • PERF_FLAG_FD_NO_GROUP:此标志允许将事件创建为事件组的一部分,但没有组长。 尚不清楚这为什么有用。
  • PERF_FLAG_FD_OUTPUT:该标志将输出从事件重新路由到组长。
  • PERF_FLAG_PID_CGROUP:该标志激活每个容器的系统范围内的监视。 容器是一种抽象,它隔离一组资源以进行更精细的控制(CPU,内存等)。 在这种模式下,仅当在受监视的CPU上运行的线程属于指定的容器(cgroup)时,才测量该事件。 通过传递在cgroupfs文件系统中其目录上打开的文件描述符来标识cgroup。 例如,如果要监视的cgroup称为test,则必须将在/ dev / cgroup / test(假定cgroupfs安装在/ dev / cgroup上)上打开的文件描述程序作为pid参数传递。 cgroup监视仅适用于系统范围的事件,因此可能需要额外的权限。(本文不讨论容器相关内容)
  • PERF_FLAG_FD_CLOEXEC:O_CLOEXEC:在linux系统中,open一个文件可以带上O_CLOEXEC标志位,这个表示位和用fcntl设置的FD_CLOEXEC有同样的作用,都是在fork的子进程中用exec系列系统调用加载新的可执行程序之前,关闭子进程中fork得到的fd。(在使用strace perf stat ls调用perf_event_open传入的即为PERF_FLAG_FD_CLOEXEC

1.1.5. struct perf_event_attr结构

1.1.5.1. 结构原型

struct perf_event_attr { 
   __u32     type;         /* Type of event */
   __u32     size;         /* Size of attribute structure */ 
   __u64     config;       /* Type-specific configuration */
   union { 
       __u64 sample_period;    /* Period of sampling */
       __u64 sample_freq;      /* Frequency of sampling */ 
   };
   __u64     sample_type;  /* Specifies values included in sample */ 
   __u64     read_format;  /* Specifies values returned in read */
   __u64     disabled       : 1,   /* off by default */ 
         inherit        : 1,   /* children inherit it */
         pinned         : 1,   /* must always be on PMU */ 
         exclusive      : 1,   /* only group on PMU */
         exclude_user   : 1,   /* don't count user */ 
         exclude_kernel : 1,   /* don't count kernel */
         exclude_hv     : 1,   /* don't count hypervisor */ 
         exclude_idle   : 1,   /* don't count when idle */
         mmap           : 1,   /* include mmap data */ 
         comm           : 1,   /* include comm data */
         freq           : 1,   /* use freq, not period */ 
         inherit_stat   : 1,   /* per task counts */
         enable_on_exec : 1,   /* next exec enables */ 
         task           : 1,   /* trace fork/exit */
         watermark      : 1,   /* wakeup_watermark */ 
         precise_ip     : 2,   /* skid constraint */
         mmap_data      : 1,   /* non-exec mmap data */ 
         sample_id_all  : 1,   /* sample_type all events */
         exclude_host   : 1,   /* don't count in host */ 
         exclude_guest  : 1,   /* don't count in guest */
         exclude_callchain_kernel : 1, /* exclude kernel callchains */ 
         exclude_callchain_user   : 1, /* exclude user callchains */ 
         __reserved_1   : 41;
   union { 
       __u32 wakeup_events;    /* wakeup every n events */
       __u32 wakeup_watermark; /* bytes before wakeup */ 
   };
   __u32     bp_type;          /* breakpoint type */
   union { 
       __u64 bp_addr;          /* breakpoint address */
       __u64 config1;          /* extension of config */ 
   };
   union { 
       __u64 bp_len;           /* breakpoint length */
       __u64 config2;          /* extension of config1 */ 
   };
   __u64   branch_sample_type; /* enum perf_branch_sample_type */ 
   __u64   sample_regs_user;   /* user regs to dump on samples */
   __u32   sample_stack_user;  /* size of stack to dump on samples */ 
   __u32   __reserved_2;       /* Align to u64 */
};

1.1.5.2. type字段

/*
 * attr.type
 */
enum perf_type_id { /* perf 类型 */
	PERF_TYPE_HARDWARE			= 0,    /* 硬件 */
	PERF_TYPE_SOFTWARE			= 1,    /* 软件 */
	PERF_TYPE_TRACEPOINT		= 2,    /* 跟踪点 /sys/bus/event_source/devices/tracepoint/type */
	PERF_TYPE_HW_CACHE			= 3,    /* 硬件cache */
	PERF_TYPE_RAW				= 4,    /* RAW/CPU /sys/bus/event_source/devices/cpu/type */
	PERF_TYPE_BREAKPOINT		= 5,    /* 断点 /sys/bus/event_source/devices/breakpoint/type */

	PERF_TYPE_MAX,				/* non-ABI */
};
  • PERF_TYPE_HARDWARE: 这表示内核提供的“通用”硬件事件之一。
  • PERF_TYPE_SOFTWARE: 这表示内核提供的一种软件定义的事件(即使没有可用的硬件支持)。
  • PERF_TYPE_TRACEPOINT: 这表示内核跟踪点基础结构提供的跟踪点。
  • PERF_TYPE_HW_CACHE: 这表示硬件高速缓存事件。 这具有特殊的编码,在config字段定义中描述。
  • PERF_TYPE_RAW: 这表示在config字段中发生的“原始”特定于实现的事件。
  • PERF_TYPE_BREAKPOINT: 这表示CPU提供的硬件断点。 断点可以是对地址的读/写访问,也可以是指令地址的执行。

动态PMU(man)
从Linux 2.6.39开始,perf_event_open()可以支持多个PMU。 为此,可以在类型字段中使用内核导出的值来指示要使用的PMU。 可以在sysfs文件系统中找到要使用的值:每个PMU实例在/sys/bus/event_source/devices下都有一个子目录。 在每个子目录中都有一个类型文件,其内容是可以在类型字段中使用的整数。 例如,/sys/bus/event_source/devices/cpu/type包含核心CPU PMU的值,通常为4。

1.1.5.3. size字段

用于向前/向后兼容性的perf_event_attr结构的大小。 使用sizeof(struct perf_event_attr)进行设置,以允许内核在编译时看到结构大小。

相关的定义PERF_ATTR_SIZE_VER0设置为64; 这是第一个发布的struct的大小。 PERF_ATTR_SIZE_VER1为72,对应于Linux 2.6.33中添加的断点。 PERF_ATTR_SIZE_VER2为80,对应于Linux 3.4中增加的分支采样。 PERF_ATR_SIZE_VER3为96,对应于Linux 3.7中的sample_regs_usersample_stack_user

#define PERF_ATTR_SIZE_VER0	64	/* sizeof first published struct */
#define PERF_ATTR_SIZE_VER1	72	/* add: config2 */
#define PERF_ATTR_SIZE_VER2	80	/* add: branch_sample_type */
#define PERF_ATTR_SIZE_VER3	96	/* add: sample_regs_user */
					/* add: sample_stack_user */
#define PERF_ATTR_SIZE_VER4	104	/* add: sample_regs_intr */
#define PERF_ATTR_SIZE_VER5	112	/* add: aux_watermark */
#define PERF_ATTR_SIZE_VER6	120	/* add: aux_sample_size */

1.1.5.4. config字段

这与type字段一起指定了您想要的事件。 在64位不足以完全指定事件的情况下,也要考虑config1和config2字段。 这些字段的编码取决于事件。
config的最高有效位(bit 63)表示特定于CPU的(原始)计数器配置数据。 如果未设置最高有效位,则接下来的7位为事件类型,其余位为事件标识符。
有多种方法可以设置配置字段,具体取决于先前描述的类型字段的值。 以下是按类型分开的config的各种可能设置。

  • 如果类型为PERF_TYPE_HARDWARE,则我们正在测量通用硬件CPU事件之一。 并非所有这些功能在所有平台上都可用。 将config设置为以下之一:
    • PERF_COUNT_HW_CPU_CYCLES总周期。警惕CPU频率缩放期间会发生什么
    • PERF_COUNT_HW_INSTRUCTIONS退休说明。请注意,这些问题可能会受到各种问题的影响,最明显的是硬件中断计数
    • PERF_COUNT_HW_CACHE_REFERENCES缓存访问。通常,这表示“最后一级缓存”访问,但这可能因您的CPU而异。这可能包括预取和一致性消息;同样,这取决于您的CPU的设计。
    • PERF_COUNT_HW_CACHE_MISSES高速缓存未命中。通常,这表明“最后一级高速缓存未命中”。它应与PERF_COUNT_HW_CACHE_REFERENCES事件一起使用,以计算高速缓存未命中率。
    • PERF_COUNT_HW_BRANCH_INSTRUCTIONS退休的分支指令。在Linux 2.6.34之前的版本中,这在AMD处理器上使用了错误的事件。
    • PERF_COUNT_HW_BRANCH_MISSES错误的分支指令。
    • PERF_COUNT_HW_BUS_CYCLES总线周期,可以与总周期不同。
    • PERF_COUNT_HW_STALLED_CYCLES_FRONTEND(从Linux 3.0开始)在发行过程中出现了停顿。
    • PERF_COUNT_HW_STALLED_CYCLES_BACKEND(从Linux 3.0开始)退休期间的停顿周期。
    • PERF_COUNT_HW_REF_CPU_CYCLES(从Linux 3.3开始)总周期;不受CPU频率缩放的影响。
/*
 * Generalized performance event event_id types, used by the
 * attr.event_id parameter of the sys_perf_event_open()
 * syscall:
 */
enum perf_hw_id {
	/*
	 * Common hardware events, generalized by the kernel:
	 */
	PERF_COUNT_HW_CPU_CYCLES		= 0,
	PERF_COUNT_HW_INSTRUCTIONS		= 1,
	PERF_COUNT_HW_CACHE_REFERENCES		= 2,
	PERF_COUNT_HW_CACHE_MISSES		= 3,
	PERF_COUNT_HW_BRANCH_INSTRUCTIONS	= 4,
	PERF_COUNT_HW_BRANCH_MISSES		= 5,
	PERF_COUNT_HW_BUS_CYCLES		= 6,
	PERF_COUNT_HW_STALLED_CYCLES_FRONTEND	= 7,
	PERF_COUNT_HW_STALLED_CYCLES_BACKEND	= 8,
	PERF_COUNT_HW_REF_CPU_CYCLES		= 9,

	PERF_COUNT_HW_MAX,			/* non-ABI */
};
  • 如果type为PERF_TYPE_SOFTWARE,则我们正在测量内核提供的软件事件。将配置设置为以下之一:
    • PERF_COUNT_SW_CPU_CLOCK这将报告CPU时钟,这是每个CPU的高分辨率计时器。
    • PERF_COUNT_SW_TASK_CLOCK这报告特定于正在运行的任务的时钟计数。
    • PERF_COUNT_SW_PAGE_FAULTS此报告页面错误的数量。
    • PERF_COUNT_SW_CONTEXT_SWITCHES这计算上下文切换。在Linux 2.6.34之前,所有这些都被报告为用户空间事件,之后它们被报告为在内核中发生。
    • PERF_COUNT_SW_CPU_MIGRATIONS这报告了进程已迁移到新CPU的次数。
    • PERF_COUNT_SW_PAGE_FAULTS_MIN此计数小页面错误的数量。这些不需要处理磁盘I / O。
    • PERF_COUNT_SW_PAGE_FAULTS_MAJ这将计算主要页面错误的数量。这些需要处理的磁盘I / O。
    • PERF_COUNT_SW_ALIGNMENT_FAULTS(从Linux 2.6.33开始)这计算对齐错误的数量。这些发生在未对齐的内存访问发生时。内核可以处理这些,但会降低性能。这仅在某些架构上才会发生(在x86上则永远不会发生)。
    • PERF_COUNT_SW_EMULATION_FAULTS(从Linux 2.6.33开始)这计算了仿真错误的数量。内核有时会捕获未执行的指令,并为用户空间模拟它们。这会对性能产生负面影响。
/*
 * Special "software" events provided by the kernel, even if the hardware
 * does not support performance events. These events measure various
 * physical and sw events of the kernel (and allow the profiling of them as
 * well):
 */
enum perf_sw_ids {
	PERF_COUNT_SW_CPU_CLOCK			= 0,
	PERF_COUNT_SW_TASK_CLOCK		= 1,
	PERF_COUNT_SW_PAGE_FAULTS		= 2,
	PERF_COUNT_SW_CONTEXT_SWITCHES		= 3,
	PERF_COUNT_SW_CPU_MIGRATIONS		= 4,
	PERF_COUNT_SW_PAGE_FAULTS_MIN		= 5,
	PERF_COUNT_SW_PAGE_FAULTS_MAJ		= 6,
	PERF_COUNT_SW_ALIGNMENT_FAULTS		= 7,
	PERF_COUNT_SW_EMULATION_FAULTS		= 8,
	PERF_COUNT_SW_DUMMY			= 9,
	PERF_COUNT_SW_BPF_OUTPUT		= 10,

	PERF_COUNT_SW_MAX,			/* non-ABI */
};

如果type为PERF_TYPE_TRACEPOINT,则我们正在测量内核跟踪点。 如果在内核中启用了ftrace,则可从debugfs /sys/kernel/debug/tracing/events/*/*/id下获取在config中使用的值,如:

[root@localhost sched_switch]# pwd
/sys/kernel/debug/tracing/events/sched/sched_switch
[root@localhost sched_switch]# more id 
326
  • 如果类型为PERF_TYPE_HW_CACHE,则我们正在测量硬件CPU缓存事件。要计算适当的配置值,请使用以下公式:
(perf_hw_cache_id)|(perf_hw_cache_op_id << 8)|(perf_hw_cache_op_result_id << 16)

其中perf_hw_cache_id是以下之一:

  • PERF_COUNT_HW_CACHE_L1D用于测量1级数据缓存
  • PERF_COUNT_HW_CACHE_L1I用于测量1级指令缓存
  • PERF_COUNT_HW_CACHE_LL用于测量最后一级缓存
  • PERF_COUNT_HW_CACHE_DTLB用于测量数据TLB
  • PERF_COUNT_HW_CACHE_ITLB用于测量指令TLB
  • PERF_COUNT_HW_CACHE_BPU用于测量分支预测单元
  • PERF_COUNT_HW_CACHE_NODE(从Linux 3.0开始),用于测量本地内存访问

perf_hw_cache_op_id是以下项之一

  • PERF_COUNT_HW_CACHE_OP_READ用于读取访问
  • PERF_COUNT_HW_CACHE_OP_WRITE用于写访问
  • PERF_COUNT_HW_CACHE_OP_PREFETCH用于预取访问

perf_hw_cache_op_result_id是以下项之一

  • PERF_COUNT_HW_CACHE_RESULT_ACCESS来衡量访问
  • PERF_COUNT_HW_CACHE_RESULT_MISS来衡量未命中率
/*
 * Generalized hardware cache events:
 *
 *       { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x
 *       { read, write, prefetch } x
 *       { accesses, misses }
 */
enum perf_hw_cache_id {
	PERF_COUNT_HW_CACHE_L1D			= 0,
	PERF_COUNT_HW_CACHE_L1I			= 1,
	PERF_COUNT_HW_CACHE_LL			= 2,
	PERF_COUNT_HW_CACHE_DTLB		= 3,
	PERF_COUNT_HW_CACHE_ITLB		= 4,
	PERF_COUNT_HW_CACHE_BPU			= 5,
	PERF_COUNT_HW_CACHE_NODE		= 6,

	PERF_COUNT_HW_CACHE_MAX,		/* non-ABI */
};

enum perf_hw_cache_op_id {
	PERF_COUNT_HW_CACHE_OP_READ		= 0,
	PERF_COUNT_HW_CACHE_OP_WRITE		= 1,
	PERF_COUNT_HW_CACHE_OP_PREFETCH		= 2,

	PERF_COUNT_HW_CACHE_OP_MAX,		/* non-ABI */
};

enum perf_hw_cache_op_result_id {
	PERF_COUNT_HW_CACHE_RESULT_ACCESS	= 0,
	PERF_COUNT_HW_CACHE_RESULT_MISS		= 1,

	PERF_COUNT_HW_CACHE_RESULT_MAX,		/* non-ABI */
};
  • 如果类型为PERF_TYPE_RAW,则需要一个自定义的“原始”配置值。 大多数CPU支持“通用”事件未涵盖的事件。 这些是实现定义的; 请参阅您的CPU手册(例如,英特尔第3B卷文档或《 AMD Bios和内核开发人员指南》)。 libpfm4库可用于从体系结构手册中的名称转换为该字段中期望的原始十六进制值perf_event_open()。

  • 如果类型为PERF_TYPE_BREAKPOINT,则将配置设置为零。 它的参数在其他地方设置。

1.1.5.5. sample_period, sample_freq字段

“采样”计数器是每N个事件生成一个中断的计数器,其中N由sample_period给出。 采样计数器的sample_period>0。发生溢出中断时,请求的数据将记录在mmap缓冲区中。 sample_type字段控制在每个中断上记录哪些数据。

如果您希望使用频率而非周期,则可以使用sample_freq。 在这种情况下,您可以设置freq标志。 内核将调整采样周期以尝试达到所需的速率。 调整率是一个计时器刻度。

1.1.5.6. sample_type字段

该字段中的各个位指定要包括在样本中的值。它们将被记录在环形缓冲区中,使用mmap(2)可在用户空间中使用。值在样本中的保存顺序在下面的MMAP布局小节中介绍;它不是枚举perf_event_sample_format顺序。

  • PERF_SAMPLE_IP记录指令指针。
  • PERF_SAMPLE_TID记录进程和线程ID。
  • PERF_SAMPLE_TIME记录时间戳。
  • PERF_SAMPLE_ADDR记录地址(如果适用)。
  • PERF_SAMPLE_READ记录组中所有事件的计数器值,而不仅仅是组长。
  • PERF_SAMPLE_CALLCHAIN记录调用链(堆栈回溯)。
  • PERF_SAMPLE_ID为打开的事件的组长记录唯一的ID。
  • PERF_SAMPLE_CPU记录CPU编号。
  • PERF_SAMPLE_PERIOD记录当前采样周期。
  • PERF_SAMPLE_STREAM_ID记录打开的事件的唯一ID。与PERF_SAMPLE_ID不同,返回的是实际ID,而不是组长。此ID与PERF_FORMAT_ID返回的ID相同。
  • PERF_SAMPLE_RAW记录其他数据(如果适用)。通常由跟踪点事件返回。
  • PERF_SAMPLE_BRANCH_STACK(从Linux 3.4开始),它提供了最近分支的记录,该记录由CPU分支采样硬件(例如Intel Last Branch Record)提供。并非所有硬件都支持此功能。有关如何过滤报告的分支的信息,请参见branch_sample_type字段。
  • PERF_SAMPLE_REGS_USER(从Linux 3.7开始)记录当前用户级别的CPU寄存器状态(调用内核之前的进程中的值)。
  • PERF_SAMPLE_STACK_USER(从Linux 3.7开始)记录用户级堆栈,从而允许展开堆栈。
  • PERF_SAMPLE_WEIGHT(从Linux 3.10开始)记录硬件提供的权重值,该权重值表示采样事件的成本。这允许硬件在配置文件中突出显示昂贵的事件。
  • PERF_SAMPLE_DATA_SRC(从Linux 3.10开始)记录数据源:与采样指令关联的数据在内存层次结构中的位置。仅当基础硬件支持此功能时,此选项才可用。
/*
 * Bits that can be set in attr.sample_type to request information
 * in the overflow packets.
 */
enum perf_event_sample_format {
	PERF_SAMPLE_IP				= 1U << 0,
	PERF_SAMPLE_TID				= 1U << 1,
	PERF_SAMPLE_TIME			= 1U << 2,
	PERF_SAMPLE_ADDR			= 1U << 3,
	PERF_SAMPLE_READ			= 1U << 4,
	PERF_SAMPLE_CALLCHAIN			= 1U << 5,
	PERF_SAMPLE_ID				= 1U << 6,
	PERF_SAMPLE_CPU				= 1U << 7,
	PERF_SAMPLE_PERIOD			= 1U << 8,
	PERF_SAMPLE_STREAM_ID			= 1U << 9,
	PERF_SAMPLE_RAW				= 1U << 10,
	PERF_SAMPLE_BRANCH_STACK		= 1U << 11,
	PERF_SAMPLE_REGS_USER			= 1U << 12,
	PERF_SAMPLE_STACK_USER			= 1U << 13,
	PERF_SAMPLE_WEIGHT			= 1U << 14,
	PERF_SAMPLE_DATA_SRC			= 1U << 15,
	PERF_SAMPLE_IDENTIFIER			= 1U << 16,
	PERF_SAMPLE_TRANSACTION			= 1U << 17,
	PERF_SAMPLE_REGS_INTR			= 1U << 18,
	PERF_SAMPLE_PHYS_ADDR			= 1U << 19,
	PERF_SAMPLE_AUX				= 1U << 20,
	PERF_SAMPLE_CGROUP			= 1U << 21,

	PERF_SAMPLE_MAX = 1U << 22,		/* non-ABI */

	__PERF_SAMPLE_CALLCHAIN_EARLY		= 1ULL << 63, /* non-ABI; internal use */
};

1.1.5.7. read_format

该字段指定perf_event_open()文件描述符上read(2)返回的数据格式。

  • PERF_FORMAT_TOTAL_TIME_ENABLED添加64位启用时间的字段。 如果PMU过量使用并且正在发生多路复用,则可将其用于计算估计总数。
  • PERF_FORMAT_TOTAL_TIME_RUNNING添加64位time_running字段。 如果PMU过量使用并且正在发生多路复用,则可将其用于计算估计总数。
  • PERF_FORMAT_ID添加一个与事件组相对应的64位唯一值。
  • PERF_FORMAT_GROUP允许通过一次读取来读取事件组中的所有计数器值。

/*
 * The format of the data returned by read() on a perf event fd,
 * as specified by attr.read_format:
 *
 * struct read_format {
 *	{ u64		value;
 *	  { u64		time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
 *	  { u64		time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
 *	  { u64		id;           } && PERF_FORMAT_ID
 *	} && !PERF_FORMAT_GROUP
 *
 *	{ u64		nr;
 *	  { u64		time_enabled; } && PERF_FORMAT_TOTAL_TIME_ENABLED
 *	  { u64		time_running; } && PERF_FORMAT_TOTAL_TIME_RUNNING
 *	  { u64		value;
 *	    { u64	id;           } && PERF_FORMAT_ID
 *	  }		cntr[nr];
 *	} && PERF_FORMAT_GROUP
 * };
 */
enum perf_event_read_format {
	PERF_FORMAT_TOTAL_TIME_ENABLED		= 1U << 0,
	PERF_FORMAT_TOTAL_TIME_RUNNING		= 1U << 1,
	PERF_FORMAT_ID				= 1U << 2,
	PERF_FORMAT_GROUP			= 1U << 3,

	PERF_FORMAT_MAX = 1U << 4,		/* non-ABI */
};

1.1.5.8. disable

禁用位指定计数器开始时是禁用还是启用。 如果禁用该事件,则以后可以通过ioctl(2),prctl(2)或enable_on_exec启用该事件。

1.1.5.9. inherit

继承位指定此计数器应对子任务以及指定任务的事件进行计数。 这仅适用于新子代,不适用于创建计数器时的任何现有子代(也不适用于现有子代的任何新子代)。
继承不适用于某些read_formats组合,例如PERF_FORMAT_GROUP。

1.1.5.10. pinned

固定位指定计数器应尽可能始终位于CPU上。 它仅适用于硬件计数器,并且仅适用于组长。 如果无法将固定计数器放到CPU上(例如,由于没有足够的硬件计数器或由于与其他事件发生冲突),则计数器进入“错误”状态,读取返回文件末尾 (即read(2)返回0),直到随后启用或禁用计数器为止。

1.1.5.11. exclusive

独占位指定当此计数器的组在CPU上时,它应该是使用CPU计数器的唯一组。 将来,这可能允许监视程序支持需要单独运行的PMU功能,以使它们不会破坏其他硬件计数器。

1.1.5.12. exclude_user

如果该位置1,则计数不包括用户空间中发生的事件。

1.1.5.13. exclude_kernel

如果该位置1,则计数不包括发生在内核空间中的事件。

1.1.5.14. exclude_hv

如果设置了此位,则计数将排除虚拟机管理程序中发生的事件。 这主要是针对具有内置支持的PMU(例如POWER)。 在大多数机器上处理虚拟机监控程序测量需要额外的支持。

1.1.5.15. exclude_idle

如果设置,则不计入CPU空闲时间。

1.1.5.16. mmap

mmap位允许记录执行mmap事件。

1.1.5.17. comm

comm位可跟踪由exec(2)和prctl(PR_SET_NAME)系统调用修改的进程命令名称。 不幸的是,对于工具而言,无法区分一个系统调用与另一个系统调用。

1.1.5.18. freq

如果设置了此位,则在设置采样间隔时将使用sample_frequency而不是sample_period。

1.1.5.19. Inherit_stat

通过该位,可以在上下文切换中保存继承任务的事件计数。 仅当设置了继承字段时,这才有意义。

1.1.5.20. enable_on_exec

如果设置了此位,则在调用exec(2)之后将自动启用计数器。

1.1.5.21. task

如果该位置1,则frok/exit通知将包含在环形缓冲区中。

1.1.5.22. watermark

如果设置,当我们越过wakeup_watermark边界时,会发生采样中断。 否则,在wakeup_events样本之后发生中断。

1.1.5.23. precision_ip

(从Linux 2.6.35开始)控制滑移量。 Skid是在感兴趣的事件发生和内核能够停止并记录该事件之间执行的指令数量。 滑移越小越好,并且可以更准确地报告哪些事件与哪些指令相对应,但是硬件通常受到限制,因为它可能很小。
其值如下:

  • 0-SAMPLE_IP可以任意打滑
  • 1-SAMPLE_IP必须持续滑动
  • 2-SAMPLE_IP请求打滑0
  • 3-SAMPLE_IP必须具有0打滑。 另请参见PERF_RECORD_MISC_EXACT_IP。

1.1.5.24. mmap_data

(自Linux 2.6.36起)mmap字段的对应部分,但允许在环形缓冲区中包含数据mmap事件。

1.1.5.25. sample_id_all

(从Linux 2.6.38开始)(如果已设置),那么如果选择了相应的sample_type,则TID,TIME,ID,CPU和STREAM_ID可以另外包含在非PERF_RECORD_SAMPLE中。

1.1.5.26. exclude_host

(从Linux 3.2开始)不衡量在VM主机上花费的时间

1.1.5.27. exclude_guest

(从Linux 3.2开始)不衡量在VM guest虚拟机上花费的时间

1.1.5.28. exclude_callchain_kernel

(从Linux 3.7开始)不包括内核调用链。

1.1.5.29. exclude_callchain_user

(从Linux 3.7开始)不包括用户调用链。

1.1.5.30. wakeup_events,wakeup_watermark

该联合会设置在发生溢出信号之前发生的采样数(wakeup_events)或字节数(wakeup_watermark)。 水印位标志选择使用哪一个。
wakeup_events仅计数PERF_RECORD_SAMPLE记录类型。 要接收每种传入的PERF_RECORD类型的信号,请将wakeup_watermark设置为1。

1.1.5.31. bp_type

(从Linux 2.6.33开始)选择断点类型。 它是以下之一:

  • HW_BREAKPOINT_EMPTY无断点
  • 当我们读取内存位置时,HW_BREAKPOINT_R计数
  • 写入内存位置时的HW_BREAKPOINT_W计数
  • 当我们读取或写入内存位置时,HW_BREAKPOINT_RW计数
  • 当我们在内存位置执行代码时,HW_BREAKPOINT_X计数
  • 这些值可以按位或进行组合,但不允许将HW_BREAKPOINT_RHW_BREAKPOINT_WHW_BREAKPOINT_X组合。
enum {
	HW_BREAKPOINT_EMPTY	= 0,
	HW_BREAKPOINT_R		= 1,
	HW_BREAKPOINT_W		= 2,
	HW_BREAKPOINT_RW	= HW_BREAKPOINT_R | HW_BREAKPOINT_W,
	HW_BREAKPOINT_X		= 4,
	HW_BREAKPOINT_INVALID   = HW_BREAKPOINT_RW | HW_BREAKPOINT_X,
};

1.1.5.32. bp_addr

(自Linux 2.6.33开始)断点的bp_addr地址。 对于执行断点,这是目标指令的内存地址; 对于读写断点,它是目标内存位置的内存地址。

1.1.5.33. config1

(自Linux 2.6.39起)config1用于设置需要额外寄存器或不适合常规config字段的事件。 Nehalem / Westmere / SandyBridge上的Raw OFFCORE_EVENTS在3.3及更高版本的内核上使用此字段。

1.1.5.34. bp_len

(从Linux 2.6.33开始)如果类型为PERF_TYPE_BREAKPOINT,则bp_len是要测量的断点的长度。 选项为HW_BREAKPOINT_LEN_1HW_BREAKPOINT_LEN_2HW_BREAKPOINT_LEN_4HW_BREAKPOINT_LEN_8。 对于执行断点,请将其设置为sizeof(long)。

enum {
	HW_BREAKPOINT_LEN_1 = 1,
	HW_BREAKPOINT_LEN_2 = 2,
	HW_BREAKPOINT_LEN_3 = 3,
	HW_BREAKPOINT_LEN_4 = 4,
	HW_BREAKPOINT_LEN_5 = 5,
	HW_BREAKPOINT_LEN_6 = 6,
	HW_BREAKPOINT_LEN_7 = 7,
	HW_BREAKPOINT_LEN_8 = 8,
};

1.1.5.35. config2 (Since Linux 2.6.39)

config2是config1字段的进一步扩展。

1.1.5.36. branch_sample_type

(自Linux 3.4开始)如果启用了PERF_SAMPLE_BRANCH_STACK,则此选项指定要在分支记录中包括哪些分支。 如果用户未明确设置特权级别,则内核将使用事件的特权级别。 事件和分支特权级别不必匹配。 尽管PERF_SAMPLE_BRANCH_ANY涵盖了所有分支类型,但是通过将零个或多个以下值进行或运算来形成该值。

  • PERF_SAMPLE_BRANCH_USER分支目标在用户空间中
  • PERF_SAMPLE_BRANCH_KERNEL分支目标在内核空间中
  • PERF_SAMPLE_BRANCH_HV分支目标在管理程序中
  • PERF_SAMPLE_BRANCH_ANY任何分支类型。
  • PERF_SAMPLE_BRANCH_ANY_CALL任何呼叫分支
  • PERF_SAMPLE_BRANCH_ANY_RETURN任何返回分支
  • PERF_SAMPLE_BRANCH_IND_CALL间接调用
  • PERF_SAMPLE_BRANCH_PLM_ALL用户,内核和HV

/*
 * values to program into branch_sample_type when PERF_SAMPLE_BRANCH is set
 *
 * If the user does not pass priv level information via branch_sample_type,
 * the kernel uses the event's priv level. Branch and event priv levels do
 * not have to match. Branch priv level is checked for permissions.
 *
 * The branch types can be combined, however BRANCH_ANY covers all types
 * of branches and therefore it supersedes all the other types.
 */
enum perf_branch_sample_type_shift {
	PERF_SAMPLE_BRANCH_USER_SHIFT		= 0, /* user branches */
	PERF_SAMPLE_BRANCH_KERNEL_SHIFT		= 1, /* kernel branches */
	PERF_SAMPLE_BRANCH_HV_SHIFT		= 2, /* hypervisor branches */

	PERF_SAMPLE_BRANCH_ANY_SHIFT		= 3, /* any branch types */
	PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT	= 4, /* any call branch */
	PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT	= 5, /* any return branch */
	PERF_SAMPLE_BRANCH_IND_CALL_SHIFT	= 6, /* indirect calls */
	PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT	= 7, /* transaction aborts */
	PERF_SAMPLE_BRANCH_IN_TX_SHIFT		= 8, /* in transaction */
	PERF_SAMPLE_BRANCH_NO_TX_SHIFT		= 9, /* not in transaction */
	PERF_SAMPLE_BRANCH_COND_SHIFT		= 10, /* conditional branches */

	PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT	= 11, /* call/ret stack */
	PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT	= 12, /* indirect jumps */
	PERF_SAMPLE_BRANCH_CALL_SHIFT		= 13, /* direct call */

	PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT	= 14, /* no flags */
	PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT	= 15, /* no cycles */

	PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT	= 16, /* save branch type */

	PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT	= 17, /* save low level index of raw branch records */

	PERF_SAMPLE_BRANCH_MAX_SHIFT		/* non-ABI */
};

enum perf_branch_sample_type {
	PERF_SAMPLE_BRANCH_USER		= 1U << PERF_SAMPLE_BRANCH_USER_SHIFT,
	PERF_SAMPLE_BRANCH_KERNEL	= 1U << PERF_SAMPLE_BRANCH_KERNEL_SHIFT,
	PERF_SAMPLE_BRANCH_HV		= 1U << PERF_SAMPLE_BRANCH_HV_SHIFT,

	PERF_SAMPLE_BRANCH_ANY		= 1U << PERF_SAMPLE_BRANCH_ANY_SHIFT,
	PERF_SAMPLE_BRANCH_ANY_CALL	= 1U << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT,
	PERF_SAMPLE_BRANCH_ANY_RETURN	= 1U << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT,
	PERF_SAMPLE_BRANCH_IND_CALL	= 1U << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT,
	PERF_SAMPLE_BRANCH_ABORT_TX	= 1U << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT,
	PERF_SAMPLE_BRANCH_IN_TX	= 1U << PERF_SAMPLE_BRANCH_IN_TX_SHIFT,
	PERF_SAMPLE_BRANCH_NO_TX	= 1U << PERF_SAMPLE_BRANCH_NO_TX_SHIFT,
	PERF_SAMPLE_

以上是关于Linux内核 eBPF基础:perfperf_event_open系统调用与用户手册详解的主要内容,如果未能解决你的问题,请参考以下文章

Linux内核 eBPF基础: 探索USDT探针

Linux内核 eBPF基础:BCC (BPF Compiler Collection)

Linux内核 eBPF基础:perf:perf性能管理单元PMU的注册

Linux内核 eBPF基础:perf:perf_event在内核中的初始化

Linux内核 eBPF基础:perfperf_event_open系统调用与用户手册详解

Linux网络协议:当eBPF遇上Linux内核网络 | Linux内核之旅