108. perf_event_open - 配置和打开性能事件 見出しへのリンク

1. 函数介绍 見出しへのリンク

perf_event_open 是一个 Linux 系统调用,用于配置和打开性能监控事件。它是 Linux Performance Events Subsystem (perf) 的核心接口,允许应用程序监控各种硬件和软件性能指标,如 CPU 周期、缓存未命中、分支预测、系统调用等。

2. 函数原型 見出しへのリンク

#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <sys/syscall.h>
#include <unistd.h>

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

注意:这不是标准 C 库函数,需要通过 syscall() 调用。

3. 功能 見出しへのリンク

创建和配置性能监控事件,返回一个文件描述符用于读取性能数据。支持监控硬件性能计数器、软件事件、跟踪点等多种类型的性能事件。

4. 参数 見出しへのリンク

  • struct perf_event_attr *attr: 指向性能事件属性结构体的指针
  • pid_t pid: 目标进程 ID
    • 0: 当前进程
    • -1: 所有进程
    • 正整数: 指定进程 ID
  • int cpu: 目标 CPU 核心
    • -1: 所有 CPU 核心
    • 非负整数: 指定 CPU 核心编号
  • int group_fd: 事件组文件描述符
    • -1: 不加入事件组
    • 其他: 加入指定的事件组
  • unsigned long flags: 控制标志
    • 0: 基本行为
    • PERF_FLAG_FD_CLOEXEC: 设置 close-on-exec 标志
    • PERF_FLAG_FD_NO_GROUP: 不加入组
    • PERF_FLAG_ONE_SHOT: 一次性事件
    • PERF_FLAG_PID_CGROUP: 使用 cgroup PID

5. perf_event_attr 结构体定义 見出しへのリンク

struct perf_event_attr {
    __u32 type;                 /* 事件类型 */
    __u64 size;                 /* 结构体大小 */
    __u64 config;               /* 事件配置 */
    union {
        __u64 sample_period;    /* 采样周期 */
        __u64 sample_freq;      /* 采样频率 */
    };
    __u64 sample_type;          /* 采样类型 */
    __u64 read_format;          /* 读取格式 */
    __u64 disabled       : 1,   /* 初始禁用 */
              inherit        : 1,   /* 子进程继承 */
              pinned         : 1,   /* 固定在计数器上 */
              exclusive      : 1,   /* 独占模式 */
              exclude_user   : 1,   /* 排除用户态 */
              exclude_kernel : 1,   /* 排除内核态 */
              exclude_hv     : 1,   /* 排除虚拟机监视器 */
              exclude_idle   : 1,   /* 排除空闲态 */
              // ... 更多标志位
              __reserved_1   : 55;
    
    __u32 bp_type;              /* 断点类型 */
    union {
        __u64 bp_addr;          /* 断点地址 */
        __u64 config1;          /* 扩展配置 */
    };
    union {
        __u64 bp_len;           /* 断点长度 */
        __u64 config2;          /* 扩展配置 */
    };
    __u64 branch_sample_type;   /* 分支采样类型 */
    __u64 sample_regs_user;     /* 用户态寄存器采样 */
    __u64 sample_stack_user;    /* 用户态栈采样 */
    __s32 clockid;              /* 时钟 ID */
    __u32 aux_watermark;        /* 辅助水印 */
    __u32 sample_max_stack;     /* 最大栈深度 */
    __u32 __reserved_2;         /* 保留字段 */
};

6. 事件类型 見出しへのリンク

// 主要事件类型:
#define PERF_TYPE_HARDWARE      0  /* 硬件事件 */
#define PERF_TYPE_SOFTWARE      1  /* 软件事件 */
#define PERF_TYPE_TRACEPOINT    2  /* 跟踪点事件 */
#define PERF_TYPE_HW_CACHE      3  /* 硬件缓存事件 */
#define PERF_TYPE_RAW           4  /* 原始硬件事件 */
#define PERF_TYPE_BREAKPOINT    5  /* 断点事件 */

// 硬件事件:
#define PERF_COUNT_HW_CPU_CYCLES         0  /* CPU 周期 */
#define PERF_COUNT_HW_INSTRUCTIONS       1  /* 指令数 */
#define PERF_COUNT_HW_CACHE_REFERENCES  2  /* 缓存引用 */
#define PERF_COUNT_HW_CACHE_MISSES      3  /* 缓存未命中 */
#define PERF_COUNT_HW_BRANCH_INSTRUCTIONS 4  /* 分支指令 */
#define PERF_COUNT_HW_BRANCH_MISSES     5  /* 分支未命中 */
#define PERF_COUNT_HW_BUS_CYCLES        6  /* 总线周期 */
#define PERF_COUNT_HW_STALLED_CYCLES_FRONTEND 7  /* 前端停顿周期 */
#define PERF_COUNT_HW_STALLED_CYCLES_BACKEND 8  /* 后端停顿周期 */
#define PERF_COUNT_HW_REF_CPU_CYCLES    9  /* 参考 CPU 周期 */

// 软件事件:
#define PERF_COUNT_SW_CPU_CLOCK         0  /* CPU 时钟 */
#define PERF_COUNT_SW_TASK_CLOCK        1  /* 任务时钟 */
#define PERF_COUNT_SW_PAGE_FAULTS       2  /* 页面错误 */
#define PERF_COUNT_SW_CONTEXT_SWITCHES  3  /* 上下文切换 */
#define PERF_COUNT_SW_CPU_MIGRATIONS    4  /* CPU 迁移 */
#define PERF_COUNT_SW_PAGE_FAULTS_MIN   5  /* 次要页面错误 */
#define PERF_COUNT_SW_PAGE_FAULTS_MAJ   6  /* 主要页面错误 */
#define PERF_COUNT_SW_ALIGNMENT_FAULTS  7  /* 对齐错误 */
#define PERF_COUNT_SW_EMULATION_FAULTS  8  /* 仿真错误 */
#define PERF_COUNT_SW_DUMMY             9  /* 虚拟事件 */

7. 返回值 見出しへのリンク

  • 成功时返回新的文件描述符(非负整数)
  • 失败时返回 -1,并设置 errno

8. 常见 errno 错误码 見出しへのリンク

  • EACCES: 权限不足
  • EBADF: 无效的 group_fd
  • EINVAL: 参数无效
  • EMFILE: 进程已打开的文件描述符达到上限
  • ENFILE: 系统已打开的文件描述符达到上限
  • ENOMEM: 内存不足
  • ENODEV: 指定的事件类型或配置不支持
  • EPERM: 操作被拒绝(需要特权权限)
  • ESRCH: 指定的进程不存在
  • E2BIG: 事件过多或配置过大
  • EOPNOTSUPP: 操作不支持

9. 相似函数,或关联函数 見出しへのリンク

  • read(): 读取性能事件数据
  • ioctl(): 控制性能事件
  • mmap(): 内存映射性能数据缓冲区
  • perf_event_open() 相关 ioctl 命令:
    • PERF_EVENT_IOC_RESET: 重置计数器
    • PERF_EVENT_IOC_ENABLE: 启用事件
    • PERF_EVENT_IOC_DISABLE: 禁用事件
    • PERF_EVENT_IOC_REFRESH: 刷新事件
    • PERF_EVENT_IOC_PERIOD: 设置采样周期
  • /usr/bin/perf: 用户态性能分析工具
  • /proc/sys/kernel/perf_event_paranoid: 性能事件安全级别
  • /proc/sys/kernel/perf_event_max_sample_rate: 最大采样率

10. 示例代码 見出しへのリンク

示例1:基本使用 - 硬件性能计数器监控 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>

#ifndef SYS_perf_event_open
# define SYS_perf_event_open 298  // x86_64 架构下的系统调用号
#endif

// perf_event_open 系统调用包装函数
int perf_event_open_wrapper(struct perf_event_attr *attr,
                           pid_t pid, int cpu, int group_fd, unsigned long flags) {
    return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags);
}

void print_perf_event_info(int fd, const char *event_name) {
    long long count;
    if (read(fd, &count, sizeof(count)) == sizeof(count)) {
        printf("%-30s: %lld\n", event_name, count);
    } else {
        printf("%-30s: 读取失败\n", event_name);
    }
}

int main() {
    printf("=== perf_event_open 基本使用演示 ===\n");
    
    // 检查权限
    int paranoid_level = 0;
    FILE *fp = fopen("/proc/sys/kernel/perf_event_paranoid", "r");
    if (fp) {
        fscanf(fp, "%d", &paranoid_level);
        fclose(fp);
        printf("性能事件安全级别: %d\n", paranoid_level);
        
        if (paranoid_level > 0 && geteuid() != 0) {
            printf("警告: 需要 root 权限或降低安全级别才能监控其他进程\n");
        }
    }
    
    // 配置硬件性能事件属性
    struct perf_event_attr pe;
    memset(&pe, 0, sizeof(pe));
    pe.size = sizeof(pe);
    pe.type = PERF_TYPE_HARDWARE;
    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    pe.disabled = 1;  // 初始禁用
    pe.exclude_kernel = 1;  // 排除内核态
    pe.exclude_hv = 1;  // 排除虚拟机监视器
    
    // 打开性能事件(监控当前进程,所有 CPU)
    int fd_instructions = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd_instructions == -1) {
        if (errno == EACCES) {
            printf("错误: 权限不足,需要 root 权限或调整 /proc/sys/kernel/perf_event_paranoid\n");
            printf("解决方法:\n");
            printf("  1. 使用 sudo 运行程序\n");
            printf("  2. 执行: echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid\n");
            exit(EXIT_FAILURE);
        } else {
            printf("打开指令计数器失败: %s\n", strerror(errno));
            exit(EXIT_FAILURE);
        }
    }
    
    printf("✓ 成功打开指令计数器 (fd: %d)\n", fd_instructions);
    
    // 配置 CPU 周期事件
    pe.config = PERF_COUNT_HW_CPU_CYCLES;
    int fd_cycles = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd_cycles == -1) {
        printf("打开 CPU 周期计数器失败: %s\n", strerror(errno));
        close(fd_instructions);
        exit(EXIT_FAILURE);
    }
    
    printf("✓ 成功打开 CPU 周期计数器 (fd: %d)\n", fd_cycles);
    
    // 配置缓存未命中事件
    pe.config = PERF_COUNT_HW_CACHE_MISSES;
    int fd_cache_misses = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd_cache_misses == -1) {
        if (errno != ENODEV) {
            printf("打开缓存未命中计数器失败: %s\n", strerror(errno));
        } else {
            printf("警告: 系统不支持缓存未命中计数器\n");
        }
    } else {
        printf("✓ 成功打开缓存未命中计数器 (fd: %d)\n", fd_cache_misses);
    }
    
    // 启用所有事件
    if (ioctl(fd_instructions, PERF_EVENT_IOC_RESET, 0) == -1 ||
        ioctl(fd_cycles, PERF_EVENT_IOC_RESET, 0) == -1 ||
        (fd_cache_misses != -1 && ioctl(fd_cache_misses, PERF_EVENT_IOC_RESET, 0) == -1)) {
        perror("重置计数器失败");
    }
    
    if (ioctl(fd_instructions, PERF_EVENT_IOC_ENABLE, 0) == -1 ||
        ioctl(fd_cycles, PERF_EVENT_IOC_ENABLE, 0) == -1 ||
        (fd_cache_misses != -1 && ioctl(fd_cache_misses, PERF_EVENT_IOC_ENABLE, 0) == -1)) {
        perror("启用计数器失败");
    }
    
    printf("✓ 成功启用所有性能计数器\n");
    
    // 执行一些工作负载
    printf("\n执行工作负载...\n");
    
    volatile long sum = 0;
    for (int i = 0; i < 1000000; i++) {
        sum += i * i;
        if (i % 100000 == 0) {
            printf("  进度: %d%%\n", i / 10000);
        }
    }
    
    printf("工作负载完成\n");
    
    // 禁用所有事件
    if (ioctl(fd_instructions, PERF_EVENT_IOC_DISABLE, 0) == -1 ||
        ioctl(fd_cycles, PERF_EVENT_IOC_DISABLE, 0) == -1 ||
        (fd_cache_misses != -1 && ioctl(fd_cache_misses, PERF_EVENT_IOC_DISABLE, 0) == -1)) {
        perror("禁用计数器失败");
    }
    
    printf("\n=== 性能统计结果 ===\n");
    
    // 读取并显示结果
    print_perf_event_info(fd_instructions, "指令数");
    print_perf_event_info(fd_cycles, "CPU 周期");
    if (fd_cache_misses != -1) {
        print_perf_event_info(fd_cache_misses, "缓存未命中");
    }
    
    // 计算 CPI (Cycles Per Instruction)
    long long instructions, cycles;
    if (read(fd_instructions, &instructions, sizeof(instructions)) == sizeof(instructions) &&
        read(fd_cycles, &cycles, sizeof(cycles)) == sizeof(cycles)) {
        if (instructions > 0) {
            double cpi = (double)cycles / instructions;
            printf("%-30s: %.2f\n", "CPI (每指令周期数)", cpi);
        }
    }
    
    // 清理资源
    close(fd_instructions);
    close(fd_cycles);
    if (fd_cache_misses != -1) {
        close(fd_cache_misses);
    }
    
    printf("✓ 性能监控完成\n");
    
    return 0;
}

示例2:软件事件和系统调用监控 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>

#ifndef SYS_perf_event_open
# define SYS_perf_event_open 298
#endif

int perf_event_open_wrapper(struct perf_event_attr *attr,
                           pid_t pid, int cpu, int group_fd, unsigned long flags) {
    return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags);
}

void demonstrate_software_events() {
    printf("=== 软件事件监控演示 ===\n");
    
    struct perf_event_attr pe;
    memset(&pe, 0, sizeof(pe));
    pe.size = sizeof(pe);
    pe.type = PERF_TYPE_SOFTWARE;
    pe.disabled = 1;
    pe.exclude_kernel = 0;  // 包含内核态
    pe.exclude_hv = 1;
    
    // 监控页面错误
    pe.config = PERF_COUNT_SW_PAGE_FAULTS;
    int fd_page_faults = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd_page_faults == -1) {
        printf("打开页面错误计数器失败: %s\n", strerror(errno));
        return;
    }
    
    printf("✓ 成功打开页面错误计数器 (fd: %d)\n", fd_page_faults);
    
    // 监控上下文切换
    pe.config = PERF_COUNT_SW_CONTEXT_SWITCHES;
    int fd_context_switches = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd_context_switches == -1) {
        printf("打开上下文切换计数器失败: %s\n", strerror(errno));
    } else {
        printf("✓ 成功打开上下文切换计数器 (fd: %d)\n", fd_context_switches);
    }
    
    // 监控 CPU 迁移
    pe.config = PERF_COUNT_SW_CPU_MIGRATIONS;
    int fd_cpu_migrations = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd_cpu_migrations == -1) {
        printf("打开 CPU 迁移计数器失败: %s\n", strerror(errno));
    } else {
        printf("✓ 成功打开 CPU 迁移计数器 (fd: %d)\n", fd_cpu_migrations);
    }
    
    // 启用所有事件
    ioctl(fd_page_faults, PERF_EVENT_IOC_RESET, 0);
    if (fd_context_switches != -1) ioctl(fd_context_switches, PERF_EVENT_IOC_RESET, 0);
    if (fd_cpu_migrations != -1) ioctl(fd_cpu_migrations, PERF_EVENT_IOC_RESET, 0);
    
    ioctl(fd_page_faults, PERF_EVENT_IOC_ENABLE, 0);
    if (fd_context_switches != -1) ioctl(fd_context_switches, PERF_EVENT_IOC_ENABLE, 0);
    if (fd_cpu_migrations != -1) ioctl(fd_cpu_migrations, PERF_EVENT_IOC_ENABLE, 0);
    
    printf("✓ 成功启用软件事件监控\n");
    
    // 执行一些会产生系统调用和页面错误的操作
    printf("\n执行会产生系统调用和页面错误的操作...\n");
    
    // 分配大量内存以产生页面错误
    size_t alloc_size = 10 * 1024 * 1024;  // 10MB
    char *memory = malloc(alloc_size);
    if (memory) {
        printf("  分配 %zu MB 内存\n", alloc_size / (1024 * 1024));
        
        // 访问内存以触发页面错误
        for (size_t i = 0; i < alloc_size; i += 4096) {
            memory[i] = (char)(i % 256);
        }
        
        printf("  访问内存以触发页面错误\n");
        free(memory);
    }
    
    // 执行系统调用
    for (int i = 0; i < 1000; i++) {
        getuid();  // 轻量级系统调用
        if (i % 200 == 0) {
            printf("  执行系统调用: %d\n", i);
        }
    }
    
    // 触发上下文切换
    printf("  触发上下文切换...\n");
    sleep(1);
    
    // 禁用所有事件
    ioctl(fd_page_faults, PERF_EVENT_IOC_DISABLE, 0);
    if (fd_context_switches != -1) ioctl(fd_context_switches, PERF_EVENT_IOC_DISABLE, 0);
    if (fd_cpu_migrations != -1) ioctl(fd_cpu_migrations, PERF_EVENT_IOC_DISABLE, 0);
    
    printf("\n=== 软件事件统计结果 ===\n");
    
    // 读取并显示结果
    long long count;
    if (read(fd_page_faults, &count, sizeof(count)) == sizeof(count)) {
        printf("页面错误次数: %lld\n", count);
    }
    
    if (fd_context_switches != -1) {
        if (read(fd_context_switches, &count, sizeof(count)) == sizeof(count)) {
            printf("上下文切换次数: %lld\n", count);
        }
    }
    
    if (fd_cpu_migrations != -1) {
        if (read(fd_cpu_migrations, &count, sizeof(count)) == sizeof(count)) {
            printf("CPU 迁移次数: %lld\n", count);
        }
    }
    
    // 清理资源
    close(fd_page_faultes);
    if (fd_context_switches != -1) close(fd_context_switches);
    if (fd_cpu_migrations != -1) close(fd_cpu_migrations);
    
    printf("✓ 软件事件监控完成\n");
}

void demonstrate_sampling() {
    printf("\n=== 采样模式演示 ===\n");
    
    struct perf_event_attr pe;
    memset(&pe, 0, sizeof(pe));
    pe.size = sizeof(pe);
    pe.type = PERF_TYPE_HARDWARE;
    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    pe.sample_period = 100000;  // 每 100,000 条指令采样一次
    pe.sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME;
    pe.disabled = 1;
    pe.exclude_kernel = 1;
    pe.exclude_hv = 1;
    
    int fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (fd == -1) {
        printf("打开采样事件失败: %s\n", strerror(errno));
        return;
    }
    
    printf("✓ 成功打开采样事件 (fd: %d)\n", fd);
    printf("  采样周期: 每 %llu 条指令\n", (unsigned long long)pe.sample_period);
    
    // 启用采样
    ioctl(fd, PERF_EVENT_IOC_RESET, 0);
    ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);
    
    printf("执行工作负载以生成采样数据...\n");
    
    volatile long sum = 0;
    for (int i = 0; i < 5000000; i++) {
        sum += i;
    }
    
    ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
    
    // 读取计数器值
    long long count;
    if (read(fd, &count, sizeof(count)) == sizeof(count)) {
        printf("总指令数: %lld\n", count);
        printf("预计采样次数: %lld\n", count / pe.sample_period);
    }
    
    close(fd);
}

int main() {
    printf("=== perf_event_open 软件事件和采样演示 ===\n");
    
    // 检查权限
    if (geteuid() != 0) {
        printf("警告: 某些功能可能需要 root 权限\n");
    }
    
    demonstrate_software_events();
    demonstrate_sampling();
    
    return 0;
}

示例3:事件组和高级监控 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>

#ifndef SYS_perf_event_open
# define SYS_perf_event_open 298
#endif

int perf_event_open_wrapper(struct perf_event_attr *attr,
                           pid_t pid, int cpu, int group_fd, unsigned long flags) {
    return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags);
}

void demonstrate_event_groups() {
    printf("=== 事件组监控演示 ===\n");
    
    struct perf_event_attr pe;
    memset(&pe, 0, sizeof(pe));
    pe.size = sizeof(pe);
    pe.type = PERF_TYPE_HARDWARE;
    pe.disabled = 1;
    pe.exclude_kernel = 1;
    pe.exclude_hv = 1;
    
    // 创建事件组组长(指令数)
    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    int group_leader_fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (group_leader_fd == -1) {
        printf("创建事件组组长失败: %s\n", strerror(errno));
        return;
    }
    
    printf("✓ 创建事件组组长 (指令数, fd: %d)\n", group_leader_fd);
    
    // 添加 CPU 周期事件到组
    pe.config = PERF_COUNT_HW_CPU_CYCLES;
    int cycles_fd = perf_event_open_wrapper(&pe, 0, -1, group_leader_fd, 0);
    if (cycles_fd == -1) {
        printf("添加 CPU 周期事件到组失败: %s\n", strerror(errno));
    } else {
        printf("✓ 添加 CPU 周期事件到组 (fd: %d)\n", cycles_fd);
    }
    
    // 添加缓存引用事件到组
    pe.config = PERF_COUNT_HW_CACHE_REFERENCES;
    int cache_refs_fd = perf_event_open_wrapper(&pe, 0, -1, group_leader_fd, 0);
    if (cache_refs_fd == -1) {
        if (errno != ENODEV) {
            printf("添加缓存引用事件到组失败: %s\n", strerror(errno));
        }
    } else {
        printf("✓ 添加缓存引用事件到组 (fd: %d)\n", cache_refs_fd);
    }
    
    // 启用整个事件组
    if (ioctl(group_leader_fd, PERF_EVENT_IOC_RESET, PERF_IOC_FLAG_GROUP) == -1 ||
        ioctl(group_leader_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP) == -1) {
        perror("启用事件组失败");
        close(group_leader_fd);
        if (cycles_fd != -1) close(cycles_fd);
        if (cache_refs_fd != -1) close(cache_refs_fd);
        return;
    }
    
    printf("✓ 成功启用事件组\n");
    
    // 执行工作负载
    printf("\n执行工作负载...\n");
    volatile long sum = 0;
    for (int i = 0; i < 2000000; i++) {
        sum += i * i;
        if (i % 500000 == 0) {
            printf("  进度: %d%%\n", i / 20000);
        }
    }
    
    printf("工作负载完成\n");
    
    // 禁用整个事件组
    if (ioctl(group_leader_fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP) == -1) {
        perror("禁用事件组失败");
    }
    
    printf("\n=== 事件组统计结果 ===\n");
    
    // 读取组数据格式
    pe.read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_GROUP;
    
    // 重新打开事件以获取组数据
    close(group_leader_fd);
    if (cycles_fd != -1) close(cycles_fd);
    if (cache_refs_fd != -1) close(cache_refs_fd);
    
    // 创建新的组事件用于读取
    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    group_leader_fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (group_leader_fd != -1) {
        pe.config = PERF_COUNT_HW_CPU_CYCLES;
        cycles_fd = perf_event_open_wrapper(&pe, 0, -1, group_leader_fd, 0);
        
        pe.config = PERF_COUNT_HW_CACHE_REFERENCES;
        cache_refs_fd = perf_event_open_wrapper(&pe, 0, -1, group_leader_fd, 0);
        
        // 启用并执行简短测试
        ioctl(group_leader_fd, PERF_EVENT_IOC_ENABLE, PERF_IOC_FLAG_GROUP);
        volatile int dummy = 0;
        for (int i = 0; i < 100000; i++) {
            dummy += i;
        }
        ioctl(group_leader_fd, PERF_EVENT_IOC_DISABLE, PERF_IOC_FLAG_GROUP);
        
        // 读取组数据
        struct {
            uint64_t nr;
            uint64_t time_enabled;
            uint64_t time_running;
            struct {
                uint64_t value;
                uint64_t id;
            } values[3];
        } group_data;
        
        ssize_t bytes_read = read(group_leader_fd, &group_data, sizeof(group_data));
        if (bytes_read == sizeof(group_data)) {
            printf("组数据读取成功 (%zd 字节)\n", bytes_read);
            printf("  事件数量: %lu\n", (unsigned long)group_data.nr);
            printf("  启用时间: %lu ns\n", (unsigned long)group_data.time_enabled);
            printf("  运行时间: %lu ns\n", (unsigned long)group_data.time_running);
            
            for (uint64_t i = 0; i < group_data.nr; i++) {
                const char *event_names[] = {"指令数", "CPU周期", "缓存引用"};
                if (i < 3) {
                    printf("  %s: %lu\n", event_names[i], (unsigned long)group_data.values[i].value);
                }
            }
        }
    }
    
    // 清理资源
    if (group_leader_fd != -1) close(group_leader_fd);
    if (cycles_fd != -1) close(cycles_fd);
    if (cache_refs_fd != -1) close(cache_refs_fd);
    
    printf("✓ 事件组监控完成\n");
}

void demonstrate_multiplexing() {
    printf("\n=== 事件复用演示 ===\n");
    
    printf("现代 CPU 的硬件性能计数器数量有限\n");
    printf("当监控的事件数量超过可用计数器时,内核会自动进行事件复用\n");
    printf("这会导致计数器值需要通过比例估算\n");
    
    // 显示系统信息
    printf("\n系统硬件计数器信息:\n");
    system("cat /sys/bus/event_source/devices/*/format/* 2>/dev/null | head -10 || echo '无法读取硬件计数器信息'");
    
    // 显示可用事件
    printf("\n可用性能事件:\n");
    system("perf list 2>/dev/null | head -15 || echo 'perf 工具不可用'");
}

int main() {
    printf("=== perf_event_open 事件组和高级监控 ===\n");
    
    demonstrate_event_groups();
    demonstrate_multiplexing();
    
    return 0;
}

示例4:性能分析和监控工具 見出しへのリンク

#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/perf_event.h>
#include <linux/hw_breakpoint.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <time.h>
#include <signal.h>

#ifndef SYS_perf_event_open
# define SYS_perf_event_open 298
#endif

volatile sig_atomic_t monitoring = 1;

void signal_handler(int sig) {
    printf("\n收到信号 %d,停止监控\n", sig);
    monitoring = 0;
}

int perf_event_open_wrapper(struct perf_event_attr *attr,
                           pid_t pid, int cpu, int group_fd, unsigned long flags) {
    return syscall(SYS_perf_event_open, attr, pid, cpu, group_fd, flags);
}

typedef struct {
    int fd;
    const char *name;
    long long initial_value;
} perf_counter_t;

void reset_counters(perf_counter_t *counters, int count) {
    for (int i = 0; i < count; i++) {
        if (counters[i].fd != -1) {
            ioctl(counters[i].fd, PERF_EVENT_IOC_RESET, 0);
        }
    }
}

void enable_counters(perf_counter_t *counters, int count) {
    for (int i = 0; i < count; i++) {
        if (counters[i].fd != -1) {
            ioctl(counters[i].fd, PERF_EVENT_IOC_ENABLE, 0);
        }
    }
}

void disable_counters(perf_counter_t *counters, int count) {
    for (int i = 0; i < count; i++) {
        if (counters[i].fd != -1) {
            ioctl(counters[i].fd, PERF_EVENT_IOC_DISABLE, 0);
        }
    }
}

void read_counters(perf_counter_t *counters, int count) {
    for (int i = 0; i < count; i++) {
        if (counters[i].fd != -1) {
            long long value;
            if (read(counters[i].fd, &value, sizeof(value)) == sizeof(value)) {
                counters[i].initial_value = value;
            }
        }
    }
}

void print_counter_stats(perf_counter_t *counters, int count, double elapsed_seconds) {
    printf("\n=== 性能统计 (持续时间: %.2f 秒) ===\n", elapsed_seconds);
    printf("%-25s %-15s %-15s\n", "事件名称", "计数值", "每秒速率");
    printf("%-25s %-15s %-15s\n", "--------", "------", "------");
    
    for (int i = 0; i < count; i++) {
        if (counters[i].fd != -1) {
            long long value = counters[i].initial_value;
            double rate = elapsed_seconds > 0 ? value / elapsed_seconds : 0;
            printf("%-25s %-15lld %-15.0f\n", 
                   counters[i].name, value, rate);
        }
    }
}

void interactive_performance_monitor() {
    printf("=== 交互式性能监控工具 ===\n");
    
    // 设置信号处理
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);
    
    // 创建性能计数器
    perf_counter_t counters[] = {
        {-1, "指令数", 0},
        {-1, "CPU 周期", 0},
        {-1, "缓存引用", 0},
        {-1, "缓存未命中", 0},
        {-1, "分支指令", 0},
        {-1, "分支未命中", 0}
    };
    
    int counter_count = sizeof(counters) / sizeof(counters[0]);
    
    // 配置和打开性能事件
    struct perf_event_attr pe;
    memset(&pe, 0, sizeof(pe));
    pe.size = sizeof(pe);
    pe.type = PERF_TYPE_HARDWARE;
    pe.disabled = 1;
    pe.exclude_kernel = 0;  // 包含内核态
    pe.exclude_hv = 1;
    
    printf("创建性能计数器:\n");
    
    // 指令数
    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    counters[0].fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (counters[0].fd != -1) {
        printf("  ✓ %s\n", counters[0].name);
    }
    
    // CPU 周期
    pe.config = PERF_COUNT_HW_CPU_CYCLES;
    counters[1].fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (counters[1].fd != -1) {
        printf("  ✓ %s\n", counters[1].name);
    }
    
    // 缓存引用
    pe.config = PERF_COUNT_HW_CACHE_REFERENCES;
    counters[2].fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (counters[2].fd != -1) {
        printf("  ✓ %s\n", counters[2].name);
    }
    
    // 缓存未命中
    pe.config = PERF_COUNT_HW_CACHE_MISSES;
    counters[3].fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (counters[3].fd != -1) {
        printf("  ✓ %s\n", counters[3].name);
    }
    
    // 分支指令
    pe.config = PERF_COUNT_HW_BRANCH_INSTRUCTIONS;
    counters[4].fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (counters[4].fd != -1) {
        printf("  ✓ %s\n", counters[4].name);
    }
    
    // 分支未命中
    pe.config = PERF_COUNT_HW_BRANCH_MISSES;
    counters[5].fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    if (counters[5].fd != -1) {
        printf("  ✓ %s\n", counters[5].name);
    }
    
    // 启用所有计数器
    reset_counters(counters, counter_count);
    enable_counters(counters, counter_count);
    
    printf("\n开始性能监控 (按 Ctrl+C 停止)...\n");
    printf("%-15s %-15s %-15s %-15s\n", "时间", "指令数", "CPU周期", "缓存未命中");
    printf("%-15s %-15s %-15s %-15s\n", "----", "----", "----", "----");
    
    time_t start_time = time(NULL);
    int sample_count = 0;
    
    while (monitoring) {
        sleep(1);
        sample_count++;
        
        // 读取当前值并显示
        if (sample_count % 5 == 0) {  // 每5秒显示一次详细信息
            long long values[6] = {0};
            int valid_counters = 0;
            
            for (int i = 0; i < counter_count; i++) {
                if (counters[i].fd != -1) {
                    long long value;
                    if (read(counters[i].fd, &value, sizeof(value)) == sizeof(value)) {
                        values[i] = value;
                        valid_counters++;
                    }
                }
            }
            
            if (valid_counters > 0) {
                time_t current_time = time(NULL);
                printf("%-15ld %-15lld %-15lld %-15lld\n",
                       (long)(current_time - start_time),
                       values[0], values[1], values[3]);
            }
        }
    }
    
    // 禁用计数器
    disable_counters(counters, counter_count);
    
    // 读取最终值
    read_counters(counters, counter_count);
    
    // 显示最终统计
    time_t end_time = time(NULL);
    double elapsed_time = difftime(end_time, start_time);
    print_counter_stats(counters, counter_count, elapsed_time);
    
    // 计算比率
    if (counters[0].initial_value > 0 && counters[1].initial_value > 0) {
        double cpi = (double)counters[1].initial_value / counters[0].initial_value;
        printf("CPI (每指令周期数): %.2f\n", cpi);
    }
    
    if (counters[2].initial_value > 0 && counters[3].initial_value > 0) {
        double miss_rate = (double)counters[3].initial_value / counters[2].initial_value * 100;
        printf("缓存未命中率: %.2f%%\n", miss_rate);
    }
    
    // 清理资源
    for (int i = 0; i < counter_count; i++) {
        if (counters[i].fd != -1) {
            close(counters[i].fd);
        }
    }
    
    printf("✓ 性能监控完成\n");
}

void demonstrate_system_wide_monitoring() {
    printf("\n=== 系统范围监控演示 ===\n");
    
    printf("系统范围性能监控需要特殊权限\n");
    
    if (geteuid() != 0) {
        printf("警告: 需要 root 权限进行系统范围监控\n");
        return;
    }
    
    // 检查安全级别
    FILE *fp = fopen("/proc/sys/kernel/perf_event_paranoid", "r");
    if (fp) {
        int paranoid_level;
        if (fscanf(fp, "%d", &paranoid_level) == 1) {
            printf("当前安全级别: %d\n", paranoid_level);
            if (paranoid_level > 0) {
                printf("建议将安全级别设置为 -1 以允许系统范围监控\n");
                printf("执行: echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid\n");
            }
        }
        fclose(fp);
    }
    
    printf("系统范围监控特性:\n");
    printf("• 监控所有进程的性能\n");
    printf("• 监控特定 CPU 核心\n");
    printf("• 需要 root 权限\n");
    printf("• 可能影响系统性能\n");
    printf("• 需要适当的安全配置\n");
}

int main() {
    printf("=== perf_event_open 性能分析和监控工具 ===\n");
    
    // 检查权限
    printf("进程信息:\n");
    printf("  PID: %d\n", getpid());
    printf("  UID: %d\n", getuid());
    printf("  EUID: %d\n", geteuid());
    
    // 检查安全级别
    FILE *fp = fopen("/proc/sys/kernel/perf_event_paranoid", "r");
    if (fp) {
        int paranoid_level;
        if (fscanf(fp, "%d", &paranoid_level) == 1) {
            printf("  性能事件安全级别: %d\n", paranoid_level);
        }
        fclose(fp);
    }
    
    // 演示系统范围监控
    demonstrate_system_wide_monitoring();
    
    // 启动交互式监控器
    char choice;
    printf("\n是否启动交互式性能监控器? (y/N): ");
    if (scanf(" %c", &choice) == 1 && (choice == 'y' || choice == 'Y')) {
        interactive_performance_monitor();
    }
    
    return 0;
}

11. 高级特性和配置 見出しへのリンク

// perf_event_attr 的高级配置示例:

// 采样配置
struct perf_event_attr sampling_pe = {
    .type = PERF_TYPE_HARDWARE,
    .config = PERF_COUNT_HW_INSTRUCTIONS,
    .sample_period = 100000,  // 每 100,000 条指令采样
    .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME,
    .read_format = PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING,
    .disabled = 1,
    .inherit = 1,  // 子进程继承
    .pinned = 1,  // 固定在计数器上
    .exclusive = 1,  // 独占模式
    .exclude_user = 0,  // 包含用户态
    .exclude_kernel = 1,  // 排除内核态
    .exclude_hv = 1,  // 排除虚拟机监视器
    .exclude_idle = 1,  // 排除空闲态
};

// 精确定位采样
struct perf_event_attr precise_pe = {
    .type = PERF_TYPE_HARDWARE,
    .config = PERF_COUNT_HW_INSTRUCTIONS,
    .precise_ip = 2,  // 需要精确采样
    .sample_period = 1000,
    .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_ADDR | PERF_SAMPLE_WEIGHT,
};

// 软件事件配置
struct perf_event_attr software_pe = {
    .type = PERF_TYPE_SOFTWARE,
    .config = PERF_COUNT_SW_PAGE_FAULTS_MIN,
    .sample_period = 100,
    .sample_type = PERF_SAMPLE_IP | PERF_SAMPLE_TID,
};

12. 实际应用场景 見出しへのリンク

场景1:应用程序性能分析 見出しへのリンク

void application_profiling() {
    // 监控应用程序的关键性能指标
    struct perf_event_attr pe = {
        .type = PERF_TYPE_HARDWARE,
        .config = PERF_COUNT_HW_INSTRUCTIONS,
        .disabled = 1,
        .exclude_kernel = 1,
        .exclude_hv = 1,
    };
    
    int fd = perf_event_open_wrapper(&pe, 0, -1, -1, 0);
    // 分析应用程序的指令执行效率
}

场景2:数据库系统优化 見出しへのリンク

void database_performance_monitoring() {
    // 监控数据库查询的缓存效率和分支预测
    struct perf_event_attr cache_pe = {
        .type = PERF_TYPE_HW_CACHE,
        .config = (PERF_COUNT_HW_CACHE_L1D | 
                   (PERF_COUNT_HW_CACHE_OP_READ << 8) |
                   (PERF_COUNT_HW_CACHE_RESULT_MISS << 16)),
        .sample_period = 1000,
    };
    
    // 优化查询计划和缓存策略
}

场景3:实时系统性能监控 見出しへのリンク

void real_time_system_monitoring() {
    // 监控实时系统的上下文切换和中断延迟
    struct perf_event_attr rt_pe = {
        .type = PERF_TYPE_SOFTWARE,
        .config = PERF_COUNT_SW_CONTEXT_SWITCHES,
        .sample_period = 1,
    };
    
    // 确保系统满足实时性要求
}

13. 注意事项 見出しへのリンク

使用 perf_event_open 时需要注意:

  1. 权限要求: 某些操作需要 root 权限或适当的安全级别配置
  2. 系统支持: 不是所有事件类型在所有系统上都支持
  3. 性能影响: 频繁的性能监控可能影响系统性能
  4. 硬件限制: 硬件性能计数器数量有限,可能导致事件复用
  5. 安全考虑: 性能数据可能包含敏感信息
  6. 资源管理: 及时关闭文件描述符避免资源泄漏

14. 系统配置检查 見出しへのリンク

# 检查性能事件支持
grep -i perf /boot/config-$(uname -r)

# 查看安全级别
cat /proc/sys/kernel/perf_event_paranoid

# 查看硬件计数器信息
cat /sys/bus/event_source/devices/*/format/*

# 检查可用事件
perf list 2>/dev/null | head -20

# 查看系统调用支持
ausyscall perf_event_open

# 检查内核版本
uname -r

15. 编译和运行 見出しへのリンク

# 编译支持 perf_event_open 的程序
gcc -D_GNU_SOURCE perf_monitor.c -o perf_monitor

# 运行需要权限的监控程序
sudo ./perf_monitor

# 调整安全级别(临时)
echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid

# 永久调整安全级别
echo 'kernel.perf_event_paranoid = -1' | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

总结 見出しへのリンク

perf_event_open 是 Linux 系统中强大的性能监控工具:

关键特性:

  1. 硬件支持: 直接访问 CPU 硬件性能计数器
  2. 软件事件: 监控系统调用、页面错误等软件事件
  3. 灵活配置: 支持采样、分组、过滤等多种配置
  4. 安全控制: 通过安全级别和权限控制访问
  5. 标准化: 提供统一的性能监控接口

主要应用:

  1. 应用程序性能分析和优化
  2. 系统性能监控和诊断
  3. 数据库和 Web 服务器优化
  4. 实时系统性能验证
  5. 编译器和运行时系统优化

使用要点:

  1. 理解不同事件类型的特点和用途
  2. 正确配置安全级别和权限
  3. 合理使用采样和事件分组
  4. 注意硬件限制和事件复用
  5. 及时清理资源避免泄漏

正确使用 perf_event_open 可以为系统性能优化提供精确的数据支持,是现代 Linux 系统管理和性能工程的重要工具。