작업 중.

NAME

perf_event_open - 성능 모니터링 설정하기

SYNOPSIS

#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);

주의: 이 시스템 호출에 대한 glibc 래퍼가 없다. NOTES 참고.

DESCRIPTION

매개변수 목록을 받은 perf_event_open()은 후속 시스템 호출(read(2), mmap(2), prctl(2), fcntl(2) 등)에 사용할 파일 디스크립터를 반환한다.

perf_event_open() 호출은 성능 정보를 계측하기 위한 파일 디스크립터를 생성한다. 각 파일 디스크립터가 측정하는 한 이벤트에 대응한다. 이를 한데 묶어서 여러 이벤트를 동시에 측정할 수 있다.

ioctl(2)이나 prctl(2)을 통해서 이벤트를 켜고 끌 수 있다. 이벤트가 꺼져 있을 때에는 수를 세거나 오버플로가 발생하지 않는다. 하지만 계속 존재하면서 그 수 값을 유지한다.

이벤트에는 계수 방식과 샘플 방식이 있다. 계수(counting) 이벤트는 발생한 이벤트 총수를 세기 위한 것이다. 계수 이벤트 결과는 일반적으로 read(2) 호출로 수집한다. 샘플(sampled) 이벤트는 주기적으로 측정치를 어떤 버퍼에 기록하고, mmap(2)을 통해 거기 접근할 수 있다.

인자

pidcpu 인자를 통해 감시할 프로세스와 CPU를 지정할 수 있다.

pid == 0이고 cpu == -1
아무 CPU에서나 호출 프로세스/스레드를 계측한다.
pid == 0이고 cpu >= 0
지정한 CPU에서 돌 때만 호출 프로세스/스레드를 계측한다.
pid > 0이고 cpu == -1
아무 CPU에서나 지정한 프로세스/스레드를 계측한다.
pid > 0이고 cpu >= 0
지정한 CPU에서 돌 때만 지정한 프로세스/스레드를 계측한다.
pid == -1이고 cpu >= 0
지정한 CPU의 모든 프로세스/스레드를 계측한다. 이를 위해선 CAP_PERFMON(리눅스 5.8부터) 또는 CAP_SYS_ADMIN 역능이나 /proc/sys/kernel/perf_event_paranoid에 1보다 작은 값이 필요하다.
pid == -1이고 cpu == -1
이 구성은 유효하지 않으며 오류를 반환하게 된다.

pid가 0보다 클 때 이 시스템 호출 수행 권한은 (리눅스 5.8부터) CAP_PERFMON과 그 전 리눅스 버전에서의 ptrace 접근 모드 PTRACE_MODE_READ_REALCREDS 검사에 따라 결정된다. ptrace(2) 참고.

group_fd 인자를 통해 이벤트 그룹을 만들 수 있다. 이벤트 그룹에는 그룹 리더인 이벤트가 하나 있다. 먼저 group_id = -1로 해서 리더를 만든다. 그리고 이어지는 perf_event_open() 호출에서 group_fd를 그룹 리더의 파일 디스크립터로 설정해서 나머지 그룹 구성원들을 만든다. (단독인 이벤트는 group_fd = -1로 해서 만들며 구성원 1개인 그룹으로 본다.) 이벤트 그룹은 한 단위로 CPU로 스케줄 한다. ...........

flags 인자는 다음 값들을 0개 이상 OR 해서 만든다.

PERF_FLAG_FD_CLOEXEC (리눅스 3.14부터)
.........
PERF_FLAG_FD_NO_GROUP
.........
PERF_FLAG_FD_OUTPUT (리눅스 2.6.35부터 동작하지 않음)
.........
PERF_FLAG_FD_CGROUP (리눅스 2.6.39부터)
.........

생성하려는 이벤트에 대한 자세한 구성 정보를 perf_event_attr 구조체로 제공한다.

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 */
          mmap2          :  1,  /* include mmap with inode data */
          comm_exec      :  1,  /* flag comm events that are
                                   due to exec */
          use_clockid    :  1,  /* use clockid for time fields */
          context_switch :  1,  /* context switch data */
          write_backward :  1,  /* Write ring buffer from end
                                   to beginning */
          namespaces     :  1,  /* include namespaces data */
          ksymbol        :  1,  /* include ksymbol events */
          bpf_event      :  1,  /* include bpf events */
          aux_output     :  1,  /* generate AUX records
                                   instead of events */
          cgroup         :  1,  /* include cgroup events */
          text_poke      :  1,  /* include text poke events */

          __reserved_1   : 30;

    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 kprobe_func;      /* for perf_kprobe */
        __u64 uprobe_path;      /* for perf_uprobe */
        __u64 config1;          /* extension of config */
    };

    union {
        __u64 bp_len;           /* breakpoint length */
        __u64 kprobe_addr;      /* with kprobe_func == NULL */
        __u64 probe_offset;     /* for perf_[k,u]probe */
        __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 */
    __s32 clockid;              /* clock to use for time fields */
    __u64 sample_regs_intr;     /* regs to dump on samples */
    __u32 aux_watermark;        /* aux bytes before wakeup */
    __u16 sample_max_stack;     /* max frames in callchain */
    __u16 __reserved_2;         /* align to u64 */

};

..........

결과 읽기

.......

.....

MMAP 레이아웃

.........

struct perf_event_mmap_page {
    __u32 version;        /* version number of this structure */
    __u32 compat_version; /* lowest version this is compat with */
    __u32 lock;           /* seqlock for synchronization */
    __u32 index;          /* hardware counter identifier */
    __s64 offset;         /* add to hardware counter value */
    __u64 time_enabled;   /* time event active */
    __u64 time_running;   /* time event on CPU */
    union {
        __u64   capabilities;
        struct {
            __u64 cap_usr_time / cap_usr_rdpmc / cap_bit0 : 1,
                  cap_bit0_is_deprecated : 1,
                  cap_user_rdpmc         : 1,
                  cap_user_time          : 1,
                  cap_user_time_zero     : 1,
        };
    };
    __u16 pmc_width;
    __u16 time_shift;
    __u32 time_mult;
    __u64 time_offset;
    __u64 __reserved[120];   /* Pad to 1 k */
    __u64 data_head;         /* head in the data section */
    __u64 data_tail;         /* user-space written tail */
    __u64 data_offset;       /* where the buffer starts */
    __u64 data_size;         /* data buffer size */
    __u64 aux_head;
    __u64 aux_tail;
    __u64 aux_offset;
    __u64 aux_size;

}

...........

넘침 처리하기

....

rdpmc 인스트럭션

........

perf_event ioctl 호출

...........

PERF_EVENT_IOC_ENABLE
........

....

prctl(2) 사용하기

........

perf_event 관련 설정 파일

/proc/sys/kernel/ 내의 파일

........

/sys/bus/event_source/devices/ 내의 파일

..........

RETURN VALUE

perf_event_open()은 새 파일 디스크립터를 반환한다. 오류가 발생하면 -1을 반환한다. (이 경우 errno를 적절히 설정한다.)

ERRORS

.............

E2BIG
....
EACCES
....

...

VERSION

리눅스 2.6.31에서 perf_event_open()이 처음 도입되었는데 perf_counter_open()라는 이름이었다. 리눅스 2.6.32에서 이름이 바뀌었다.

CONFORMING TO

perf_event_open() 시스템 호출은 리눅스 전용이므로 이식성이 있어야 하는 프로그램에서는 사용하지 말아야 한다.

NOTES

glibc에서 이 시스템 호출의 래퍼를 제공하지 않는다. syscall(2)을 이용해 호출해야 한다. 아래 예를 보라.

perf_event_open() 지원 활성화 여부를 알아내는 공식적인 방법은 /proc/sys/kernel/perf_event_paranoid 파일이 존재하는지 확인하는 것이다.

BUGS

..........

EXAMPLES

다음은 printf(3) 호출의 총 인스트럭션 수를 측정하는 짧은 예이다.

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <linux/perf_event.h>
#include <asm/unistd.h>

static long
perf_event_open(struct perf_event_attr *hw_event, pid_t pid,
                int cpu, int group_fd, unsigned long flags)
{
    int ret;

    ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
                   group_fd, flags);
    return ret;
}

int
main(int argc, char **argv)
{
    struct perf_event_attr pe;
    long long count;
    int fd;

    memset(&pe, 0, sizeof(pe));
    pe.type = PERF_TYPE_HARDWARE;
    pe.size = sizeof(pe);
    pe.config = PERF_COUNT_HW_INSTRUCTIONS;
    pe.disabled = 1;
    pe.exclude_kernel = 1;
    pe.exclude_hv = 1;

    fd = perf_event_open(&pe, 0, -1, -1, 0);
    if (fd == -1) {
       fprintf(stderr, "Error opening leader %llx\n", pe.config);
       exit(EXIT_FAILURE);
    }

    ioctl(fd, PERF_EVENT_IOC_RESET, 0);
    ioctl(fd, PERF_EVENT_IOC_ENABLE, 0);

    printf("Measuring instruction count for this printf\n");

    ioctl(fd, PERF_EVENT_IOC_DISABLE, 0);
    read(fd, &count, sizeof(count));

    printf("Used %lld instructions\n", count);

    close(fd);
}

SEE ALSO

perf(1), fcntl(2), mmap(2), open(2), prctl(2), read(2)

커널 소스 트리의 Documentation/admin-guide/perf-security.rst


2021-03-22