diff --git a/lib/std/os.zig b/lib/std/os.zig index 213236af45..f167e47e2a 100644 --- a/lib/std/os.zig +++ b/lib/std/os.zig @@ -6349,3 +6349,87 @@ pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) Madv else => |err| return unexpectedErrno(err), } } + +pub const PerfEventOpenError = error{ + /// Returned if the perf_event_attr size value is too small (smaller + /// than PERF_ATTR_SIZE_VER0), too big (larger than the page size), + /// or larger than the kernel supports and the extra bytes are not + /// zero. When E2BIG is returned, the perf_event_attr size field is + /// overwritten by the kernel to be the size of the structure it was + /// expecting. + TooBig, + /// Returned when the requested event requires CAP_SYS_ADMIN permis‐ + /// sions (or a more permissive perf_event paranoid setting). Some + /// common cases where an unprivileged process may encounter this + /// error: attaching to a process owned by a different user; moni‐ + /// toring all processes on a given CPU (i.e., specifying the pid + /// argument as -1); and not setting exclude_kernel when the para‐ + /// noid setting requires it. + /// Also: + /// Returned on many (but not all) architectures when an unsupported + /// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐ + /// ting is specified. + /// It can also happen, as with EACCES, when the requested event re‐ + /// quires CAP_SYS_ADMIN permissions (or a more permissive + /// perf_event paranoid setting). This includes setting a break‐ + /// point on a kernel address, and (since Linux 3.13) setting a ker‐ + /// nel function-trace tracepoint. + PermissionDenied, + /// Returned if another event already has exclusive access to the + /// PMU. + DeviceBusy, + /// Each opened event uses one file descriptor. If a large number + /// of events are opened, the per-process limit on the number of + /// open file descriptors will be reached, and no more events can be + /// created. + ProcessResources, + EventRequiresUnsupportedCpuFeature, + /// Returned if you try to add more breakpoint + /// events than supported by the hardware. + TooManyBreakpoints, + /// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it + /// is not supported by hardware. + SampleStackNotSupported, + /// Returned if an event requiring a specific hardware feature is + /// requested but there is no hardware support. This includes re‐ + /// questing low-skid events if not supported, branch tracing if it + /// is not available, sampling if no PMU interrupt is available, and + /// branch stacks for software events. + EventNotSupported, + /// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐ + /// ple_max_stack is larger than the maximum specified in + /// /proc/sys/kernel/perf_event_max_stack. + SampleMaxStackOverflow, + /// Returned if attempting to attach to a process that does not exist. + ProcessNotFound, +} || UnexpectedError; + +pub fn perf_event_open( + attr: *linux.perf_event_attr, + pid: pid_t, + cpu: i32, + group_fd: fd_t, + flags: usize, +) PerfEventOpenError!fd_t { + const rc = system.perf_event_open(attr, pid, cpu, group_fd, flags); + switch (errno(rc)) { + .SUCCESS => return @intCast(fd_t, rc), + .@"2BIG" => return error.TooBig, + .ACCES => return error.PermissionDenied, + .BADF => unreachable, // group_fd file descriptor is not valid. + .BUSY => return error.DeviceBusy, + .FAULT => unreachable, // Segmentation fault. + .INVAL => unreachable, // Bad attr settings. + .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe. + .MFILE => return error.ProcessResources, + .NODEV => return error.EventRequiresUnsupportedCpuFeature, + .NOENT => unreachable, // Invalid type setting. + .NOSPC => return error.TooManyBreakpoints, + .NOSYS => return error.SampleStackNotSupported, + .OPNOTSUPP => return error.EventNotSupported, + .OVERFLOW => return error.SampleMaxStackOverflow, + .PERM => return error.PermissionDenied, + .SRCH => return error.ProcessNotFound, + else => |err| return unexpectedErrno(err), + } +} diff --git a/lib/std/os/linux.zig b/lib/std/os/linux.zig index 7e482fe2f4..9f5ff30694 100644 --- a/lib/std/os/linux.zig +++ b/lib/std/os/linux.zig @@ -1638,6 +1638,23 @@ pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize { } } +pub fn perf_event_open( + attr: *perf_event_attr, + pid: pid_t, + cpu: i32, + group_fd: fd_t, + flags: usize, +) usize { + return syscall5( + .perf_event_open, + @ptrToInt(attr), + @bitCast(usize, @as(isize, pid)), + @bitCast(usize, @as(isize, cpu)), + @bitCast(usize, @as(isize, group_fd)), + flags, + ); +} + pub const E = switch (native_arch) { .mips, .mipsel => @import("linux/errno/mips.zig").E, .sparc, .sparcel, .sparcv9 => @import("linux/errno/sparc.zig").E, @@ -4925,3 +4942,261 @@ pub const rtnl_link_stats64 = extern struct { /// dropped, no handler found rx_nohandler: u64, }; + +pub const perf_event_attr = extern struct { + /// Major type: hardware/software/tracepoint/etc. + type: PERF.TYPE = undefined, + /// Size of the attr structure, for fwd/bwd compat. + size: u32 = @sizeOf(perf_event_attr), + /// Type specific configuration information. + config: u64 = 0, + + sample_period_or_freq: u64 = 0, + sample_type: u64 = 0, + read_format: u64 = 0, + + flags: packed struct { + /// off by default + disabled: bool = false, + /// children inherit it + inherit: bool = false, + /// must always be on PMU + pinned: bool = false, + /// only group on PMU + exclusive: bool = false, + /// don't count user + exclude_user: bool = false, + /// ditto kernel + exclude_kernel: bool = false, + /// ditto hypervisor + exclude_hv: bool = false, + /// don't count when idle + exclude_idle: bool = false, + /// include mmap data + mmap: bool = false, + /// include comm data + comm: bool = false, + /// use freq, not period + freq: bool = false, + /// per task counts + inherit_stat: bool = false, + /// next exec enables + enable_on_exec: bool = false, + /// trace fork/exit + task: bool = false, + /// wakeup_watermark + watermark: bool = false, + /// precise_ip: + /// + /// 0 - SAMPLE_IP can have arbitrary skid + /// 1 - SAMPLE_IP must have constant skid + /// 2 - SAMPLE_IP requested to have 0 skid + /// 3 - SAMPLE_IP must have 0 skid + /// + /// See also PERF_RECORD_MISC_EXACT_IP + /// skid constraint + precise_ip: u2 = 0, + /// non-exec mmap data + mmap_data: bool = false, + /// sample_type all events + sample_id_all: bool = false, + + /// don't count in host + exclude_host: bool = false, + /// don't count in guest + exclude_guest: bool = false, + + /// exclude kernel callchains + exclude_callchain_kernel: bool = false, + /// exclude user callchains + exclude_callchain_user: bool = false, + /// include mmap with inode data + mmap2: bool = false, + /// flag comm events that are due to an exec + comm_exec: bool = false, + /// use @clockid for time fields + use_clockid: bool = false, + /// context switch data + context_switch: bool = false, + /// Write ring buffer from end to beginning + write_backward: bool = false, + /// include namespaces data + namespaces: bool = false, + + __reserved_1: u35 = 0, + } = .{}, + /// wakeup every n events, or + /// bytes before wakeup + wakeup_events_or_watermark: u32 = 0, + + bp_type: u32 = 0, + + /// This field is also used for: + /// bp_addr + /// kprobe_func for perf_kprobe + /// uprobe_path for perf_uprobe + config1: u64 = 0, + /// This field is also used for: + /// bp_len + /// kprobe_addr when kprobe_func == null + /// probe_offset for perf_[k,u]probe + config2: u64 = 0, + + /// enum perf_branch_sample_type + branch_sample_type: u64 = 0, + + /// Defines set of user regs to dump on samples. + /// See asm/perf_regs.h for details. + sample_regs_user: u64 = 0, + + /// Defines size of the user stack to dump on samples. + sample_stack_user: u32 = 0, + + clockid: i32 = 0, + /// Defines set of regs to dump for each sample + /// state captured on: + /// - precise = 0: PMU interrupt + /// - precise > 0: sampled instruction + /// + /// See asm/perf_regs.h for details. + sample_regs_intr: u64 = 0, + + /// Wakeup watermark for AUX area + aux_watermark: u32 = 0, + sample_max_stack: u16 = 0, + /// Align to u64 + __reserved_2: u16 = 0, +}; + +pub const PERF = struct { + pub const TYPE = enum(u32) { + HARDWARE, + SOFTWARE, + TRACEPOINT, + HW_CACHE, + RAW, + BREAKPOINT, + MAX, + }; + + pub const COUNT = struct { + pub const HW = enum(u32) { + CPU_CYCLES, + INSTRUCTIONS, + CACHE_REFERENCES, + CACHE_MISSES, + BRANCH_INSTRUCTIONS, + BRANCH_MISSES, + BUS_CYCLES, + STALLED_CYCLES_FRONTEND, + STALLED_CYCLES_BACKEND, + REF_CPU_CYCLES, + MAX, + + pub const CACHE = enum(u32) { + L1D, + L1I, + LL, + DTLB, + ITLB, + BPU, + NODE, + MAX, + + pub const OP = enum(u32) { + READ, + WRITE, + PREFETCH, + MAX, + }; + + pub const RESULT = enum(u32) { + ACCESS, + MISS, + MAX, + }; + }; + }; + + pub const SW = enum(u32) { + CPU_CLOCK, + TASK_CLOCK, + PAGE_FAULTS, + CONTEXT_SWITCHES, + CPU_MIGRATIONS, + PAGE_FAULTS_MIN, + PAGE_FAULTS_MAJ, + ALIGNMENT_FAULTS, + EMULATION_FAULTS, + DUMMY, + BPF_OUTPUT, + MAX, + }; + }; + + pub const SAMPLE = struct { + pub const IP = 1; + pub const TID = 2; + pub const TIME = 4; + pub const ADDR = 8; + pub const READ = 16; + pub const CALLCHAIN = 32; + pub const ID = 64; + pub const CPU = 128; + pub const PERIOD = 256; + pub const STREAM_ID = 512; + pub const RAW = 1024; + pub const BRANCH_STACK = 2048; + pub const REGS_USER = 4096; + pub const STACK_USER = 8192; + pub const WEIGHT = 16384; + pub const DATA_SRC = 32768; + pub const IDENTIFIER = 65536; + pub const TRANSACTION = 131072; + pub const REGS_INTR = 262144; + pub const PHYS_ADDR = 524288; + pub const MAX = 1048576; + + pub const BRANCH = struct { + pub const USER = 1 << 0; + pub const KERNEL = 1 << 1; + pub const HV = 1 << 2; + pub const ANY = 1 << 3; + pub const ANY_CALL = 1 << 4; + pub const ANY_RETURN = 1 << 5; + pub const IND_CALL = 1 << 6; + pub const ABORT_TX = 1 << 7; + pub const IN_TX = 1 << 8; + pub const NO_TX = 1 << 9; + pub const COND = 1 << 10; + pub const CALL_STACK = 1 << 11; + pub const IND_JUMP = 1 << 12; + pub const CALL = 1 << 13; + pub const NO_FLAGS = 1 << 14; + pub const NO_CYCLES = 1 << 15; + pub const TYPE_SAVE = 1 << 16; + pub const MAX = 1 << 17; + }; + }; + + pub const FLAG = struct { + pub const FD_NO_GROUP = 1 << 0; + pub const FD_OUTPUT = 1 << 1; + pub const PID_CGROUP = 1 << 2; + pub const FD_CLOEXEC = 1 << 3; + }; + + pub const EVENT_IOC = struct { + pub const ENABLE = 9216; + pub const DISABLE = 9217; + pub const REFRESH = 9218; + pub const RESET = 9219; + pub const PERIOD = 1074275332; + pub const SET_OUTPUT = 9221; + pub const SET_FILTER = 1074275334; + pub const SET_BPF = 1074013192; + pub const PAUSE_OUTPUT = 1074013193; + pub const QUERY_BPF = 3221758986; + pub const MODIFY_ATTRIBUTES = 1074275339; + }; +};