authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-01 14:45:05-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-11-01 14:45:05-07:00
log63f4778827bf7e48e7b451d3d04dc1a74126a6d3
tree6a63513d8f7b2853cecd9ee6d5775471bdd32bbf
parent70ef9bc75c42ec00e9d4231a2e1f1dca84144748

std: add Linux perf syscall bits

Example usage: ```zig const std = @import("std"); const PERF = std.os.linux.PERF; const assert = std.debug.assert; test "perf" { var attr: std.os.linux.perf_event_attr = .{ .type = PERF.TYPE.HARDWARE, .config = @enumToInt(PERF.COUNT.HW.INSTRUCTIONS), .flags = .{ .disabled = true, .exclude_kernel = true, .exclude_hv = true, }, }; const fd = try std.os.perf_event_open(&attr, 0, -1, -1, PERF.FLAG.FD_CLOEXEC); defer std.os.close(fd); _ = std.os.linux.ioctl(fd, PERF.EVENT_IOC.RESET, 0); _ = std.os.linux.ioctl(fd, PERF.EVENT_IOC.ENABLE, 0); long(); _ = std.os.linux.ioctl(fd, PERF.EVENT_IOC.DISABLE, 0); var result: usize = 0; assert((try std.os.read(fd, std.mem.asBytes(&result))) == @sizeOf(usize)); std.debug.print("instruction count: {d}\n", .{result}); } fn long() void { var i: usize = 0; while (i < 100000) : (i += 1) {} } ```

2 files changed, 359 insertions(+), 0 deletions(-)

lib/std/os.zig+84
......@@ -6349,3 +6349,87 @@ pub fn madvise(ptr: [*]align(mem.page_size) u8, length: usize, advice: u32) Madv
63496349 else => |err| return unexpectedErrno(err),
63506350 }
63516351}
6352
6353pub const PerfEventOpenError = error{
6354 /// Returned if the perf_event_attr size value is too small (smaller
6355 /// than PERF_ATTR_SIZE_VER0), too big (larger than the page size),
6356 /// or larger than the kernel supports and the extra bytes are not
6357 /// zero. When E2BIG is returned, the perf_event_attr size field is
6358 /// overwritten by the kernel to be the size of the structure it was
6359 /// expecting.
6360 TooBig,
6361 /// Returned when the requested event requires CAP_SYS_ADMIN permis‐
6362 /// sions (or a more permissive perf_event paranoid setting). Some
6363 /// common cases where an unprivileged process may encounter this
6364 /// error: attaching to a process owned by a different user; moni‐
6365 /// toring all processes on a given CPU (i.e., specifying the pid
6366 /// argument as -1); and not setting exclude_kernel when the para‐
6367 /// noid setting requires it.
6368 /// Also:
6369 /// Returned on many (but not all) architectures when an unsupported
6370 /// exclude_hv, exclude_idle, exclude_user, or exclude_kernel set‐
6371 /// ting is specified.
6372 /// It can also happen, as with EACCES, when the requested event re‐
6373 /// quires CAP_SYS_ADMIN permissions (or a more permissive
6374 /// perf_event paranoid setting). This includes setting a break‐
6375 /// point on a kernel address, and (since Linux 3.13) setting a ker‐
6376 /// nel function-trace tracepoint.
6377 PermissionDenied,
6378 /// Returned if another event already has exclusive access to the
6379 /// PMU.
6380 DeviceBusy,
6381 /// Each opened event uses one file descriptor. If a large number
6382 /// of events are opened, the per-process limit on the number of
6383 /// open file descriptors will be reached, and no more events can be
6384 /// created.
6385 ProcessResources,
6386 EventRequiresUnsupportedCpuFeature,
6387 /// Returned if you try to add more breakpoint
6388 /// events than supported by the hardware.
6389 TooManyBreakpoints,
6390 /// Returned if PERF_SAMPLE_STACK_USER is set in sample_type and it
6391 /// is not supported by hardware.
6392 SampleStackNotSupported,
6393 /// Returned if an event requiring a specific hardware feature is
6394 /// requested but there is no hardware support. This includes re‐
6395 /// questing low-skid events if not supported, branch tracing if it
6396 /// is not available, sampling if no PMU interrupt is available, and
6397 /// branch stacks for software events.
6398 EventNotSupported,
6399 /// Returned if PERF_SAMPLE_CALLCHAIN is requested and sam‐
6400 /// ple_max_stack is larger than the maximum specified in
6401 /// /proc/sys/kernel/perf_event_max_stack.
6402 SampleMaxStackOverflow,
6403 /// Returned if attempting to attach to a process that does not exist.
6404 ProcessNotFound,
6405} || UnexpectedError;
6406
6407pub fn perf_event_open(
6408 attr: *linux.perf_event_attr,
6409 pid: pid_t,
6410 cpu: i32,
6411 group_fd: fd_t,
6412 flags: usize,
6413) PerfEventOpenError!fd_t {
6414 const rc = system.perf_event_open(attr, pid, cpu, group_fd, flags);
6415 switch (errno(rc)) {
6416 .SUCCESS => return @intCast(fd_t, rc),
6417 .@"2BIG" => return error.TooBig,
6418 .ACCES => return error.PermissionDenied,
6419 .BADF => unreachable, // group_fd file descriptor is not valid.
6420 .BUSY => return error.DeviceBusy,
6421 .FAULT => unreachable, // Segmentation fault.
6422 .INVAL => unreachable, // Bad attr settings.
6423 .INTR => unreachable, // Mixed perf and ftrace handling for a uprobe.
6424 .MFILE => return error.ProcessResources,
6425 .NODEV => return error.EventRequiresUnsupportedCpuFeature,
6426 .NOENT => unreachable, // Invalid type setting.
6427 .NOSPC => return error.TooManyBreakpoints,
6428 .NOSYS => return error.SampleStackNotSupported,
6429 .OPNOTSUPP => return error.EventNotSupported,
6430 .OVERFLOW => return error.SampleMaxStackOverflow,
6431 .PERM => return error.PermissionDenied,
6432 .SRCH => return error.ProcessNotFound,
6433 else => |err| return unexpectedErrno(err),
6434 }
6435}
lib/std/os/linux.zig+275
......@@ -1638,6 +1638,23 @@ pub fn fadvise(fd: fd_t, offset: i64, len: i64, advice: usize) usize {
16381638 }
16391639}
16401640
1641pub fn perf_event_open(
1642 attr: *perf_event_attr,
1643 pid: pid_t,
1644 cpu: i32,
1645 group_fd: fd_t,
1646 flags: usize,
1647) usize {
1648 return syscall5(
1649 .perf_event_open,
1650 @ptrToInt(attr),
1651 @bitCast(usize, @as(isize, pid)),
1652 @bitCast(usize, @as(isize, cpu)),
1653 @bitCast(usize, @as(isize, group_fd)),
1654 flags,
1655 );
1656}
1657
16411658pub const E = switch (native_arch) {
16421659 .mips, .mipsel => @import("linux/errno/mips.zig").E,
16431660 .sparc, .sparcel, .sparcv9 => @import("linux/errno/sparc.zig").E,
......@@ -4925,3 +4942,261 @@ pub const rtnl_link_stats64 = extern struct {
49254942 /// dropped, no handler found
49264943 rx_nohandler: u64,
49274944};
4945
4946pub const perf_event_attr = extern struct {
4947 /// Major type: hardware/software/tracepoint/etc.
4948 type: PERF.TYPE = undefined,
4949 /// Size of the attr structure, for fwd/bwd compat.
4950 size: u32 = @sizeOf(perf_event_attr),
4951 /// Type specific configuration information.
4952 config: u64 = 0,
4953
4954 sample_period_or_freq: u64 = 0,
4955 sample_type: u64 = 0,
4956 read_format: u64 = 0,
4957
4958 flags: packed struct {
4959 /// off by default
4960 disabled: bool = false,
4961 /// children inherit it
4962 inherit: bool = false,
4963 /// must always be on PMU
4964 pinned: bool = false,
4965 /// only group on PMU
4966 exclusive: bool = false,
4967 /// don't count user
4968 exclude_user: bool = false,
4969 /// ditto kernel
4970 exclude_kernel: bool = false,
4971 /// ditto hypervisor
4972 exclude_hv: bool = false,
4973 /// don't count when idle
4974 exclude_idle: bool = false,
4975 /// include mmap data
4976 mmap: bool = false,
4977 /// include comm data
4978 comm: bool = false,
4979 /// use freq, not period
4980 freq: bool = false,
4981 /// per task counts
4982 inherit_stat: bool = false,
4983 /// next exec enables
4984 enable_on_exec: bool = false,
4985 /// trace fork/exit
4986 task: bool = false,
4987 /// wakeup_watermark
4988 watermark: bool = false,
4989 /// precise_ip:
4990 ///
4991 /// 0 - SAMPLE_IP can have arbitrary skid
4992 /// 1 - SAMPLE_IP must have constant skid
4993 /// 2 - SAMPLE_IP requested to have 0 skid
4994 /// 3 - SAMPLE_IP must have 0 skid
4995 ///
4996 /// See also PERF_RECORD_MISC_EXACT_IP
4997 /// skid constraint
4998 precise_ip: u2 = 0,
4999 /// non-exec mmap data
5000 mmap_data: bool = false,
5001 /// sample_type all events
5002 sample_id_all: bool = false,
5003
5004 /// don't count in host
5005 exclude_host: bool = false,
5006 /// don't count in guest
5007 exclude_guest: bool = false,
5008
5009 /// exclude kernel callchains
5010 exclude_callchain_kernel: bool = false,
5011 /// exclude user callchains
5012 exclude_callchain_user: bool = false,
5013 /// include mmap with inode data
5014 mmap2: bool = false,
5015 /// flag comm events that are due to an exec
5016 comm_exec: bool = false,
5017 /// use @clockid for time fields
5018 use_clockid: bool = false,
5019 /// context switch data
5020 context_switch: bool = false,
5021 /// Write ring buffer from end to beginning
5022 write_backward: bool = false,
5023 /// include namespaces data
5024 namespaces: bool = false,
5025
5026 __reserved_1: u35 = 0,
5027 } = .{},
5028 /// wakeup every n events, or
5029 /// bytes before wakeup
5030 wakeup_events_or_watermark: u32 = 0,
5031
5032 bp_type: u32 = 0,
5033
5034 /// This field is also used for:
5035 /// bp_addr
5036 /// kprobe_func for perf_kprobe
5037 /// uprobe_path for perf_uprobe
5038 config1: u64 = 0,
5039 /// This field is also used for:
5040 /// bp_len
5041 /// kprobe_addr when kprobe_func == null
5042 /// probe_offset for perf_[k,u]probe
5043 config2: u64 = 0,
5044
5045 /// enum perf_branch_sample_type
5046 branch_sample_type: u64 = 0,
5047
5048 /// Defines set of user regs to dump on samples.
5049 /// See asm/perf_regs.h for details.
5050 sample_regs_user: u64 = 0,
5051
5052 /// Defines size of the user stack to dump on samples.
5053 sample_stack_user: u32 = 0,
5054
5055 clockid: i32 = 0,
5056 /// Defines set of regs to dump for each sample
5057 /// state captured on:
5058 /// - precise = 0: PMU interrupt
5059 /// - precise > 0: sampled instruction
5060 ///
5061 /// See asm/perf_regs.h for details.
5062 sample_regs_intr: u64 = 0,
5063
5064 /// Wakeup watermark for AUX area
5065 aux_watermark: u32 = 0,
5066 sample_max_stack: u16 = 0,
5067 /// Align to u64
5068 __reserved_2: u16 = 0,
5069};
5070
5071pub const PERF = struct {
5072 pub const TYPE = enum(u32) {
5073 HARDWARE,
5074 SOFTWARE,
5075 TRACEPOINT,
5076 HW_CACHE,
5077 RAW,
5078 BREAKPOINT,
5079 MAX,
5080 };
5081
5082 pub const COUNT = struct {
5083 pub const HW = enum(u32) {
5084 CPU_CYCLES,
5085 INSTRUCTIONS,
5086 CACHE_REFERENCES,
5087 CACHE_MISSES,
5088 BRANCH_INSTRUCTIONS,
5089 BRANCH_MISSES,
5090 BUS_CYCLES,
5091 STALLED_CYCLES_FRONTEND,
5092 STALLED_CYCLES_BACKEND,
5093 REF_CPU_CYCLES,
5094 MAX,
5095
5096 pub const CACHE = enum(u32) {
5097 L1D,
5098 L1I,
5099 LL,
5100 DTLB,
5101 ITLB,
5102 BPU,
5103 NODE,
5104 MAX,
5105
5106 pub const OP = enum(u32) {
5107 READ,
5108 WRITE,
5109 PREFETCH,
5110 MAX,
5111 };
5112
5113 pub const RESULT = enum(u32) {
5114 ACCESS,
5115 MISS,
5116 MAX,
5117 };
5118 };
5119 };
5120
5121 pub const SW = enum(u32) {
5122 CPU_CLOCK,
5123 TASK_CLOCK,
5124 PAGE_FAULTS,
5125 CONTEXT_SWITCHES,
5126 CPU_MIGRATIONS,
5127 PAGE_FAULTS_MIN,
5128 PAGE_FAULTS_MAJ,
5129 ALIGNMENT_FAULTS,
5130 EMULATION_FAULTS,
5131 DUMMY,
5132 BPF_OUTPUT,
5133 MAX,
5134 };
5135 };
5136
5137 pub const SAMPLE = struct {
5138 pub const IP = 1;
5139 pub const TID = 2;
5140 pub const TIME = 4;
5141 pub const ADDR = 8;
5142 pub const READ = 16;
5143 pub const CALLCHAIN = 32;
5144 pub const ID = 64;
5145 pub const CPU = 128;
5146 pub const PERIOD = 256;
5147 pub const STREAM_ID = 512;
5148 pub const RAW = 1024;
5149 pub const BRANCH_STACK = 2048;
5150 pub const REGS_USER = 4096;
5151 pub const STACK_USER = 8192;
5152 pub const WEIGHT = 16384;
5153 pub const DATA_SRC = 32768;
5154 pub const IDENTIFIER = 65536;
5155 pub const TRANSACTION = 131072;
5156 pub const REGS_INTR = 262144;
5157 pub const PHYS_ADDR = 524288;
5158 pub const MAX = 1048576;
5159
5160 pub const BRANCH = struct {
5161 pub const USER = 1 << 0;
5162 pub const KERNEL = 1 << 1;
5163 pub const HV = 1 << 2;
5164 pub const ANY = 1 << 3;
5165 pub const ANY_CALL = 1 << 4;
5166 pub const ANY_RETURN = 1 << 5;
5167 pub const IND_CALL = 1 << 6;
5168 pub const ABORT_TX = 1 << 7;
5169 pub const IN_TX = 1 << 8;
5170 pub const NO_TX = 1 << 9;
5171 pub const COND = 1 << 10;
5172 pub const CALL_STACK = 1 << 11;
5173 pub const IND_JUMP = 1 << 12;
5174 pub const CALL = 1 << 13;
5175 pub const NO_FLAGS = 1 << 14;
5176 pub const NO_CYCLES = 1 << 15;
5177 pub const TYPE_SAVE = 1 << 16;
5178 pub const MAX = 1 << 17;
5179 };
5180 };
5181
5182 pub const FLAG = struct {
5183 pub const FD_NO_GROUP = 1 << 0;
5184 pub const FD_OUTPUT = 1 << 1;
5185 pub const PID_CGROUP = 1 << 2;
5186 pub const FD_CLOEXEC = 1 << 3;
5187 };
5188
5189 pub const EVENT_IOC = struct {
5190 pub const ENABLE = 9216;
5191 pub const DISABLE = 9217;
5192 pub const REFRESH = 9218;
5193 pub const RESET = 9219;
5194 pub const PERIOD = 1074275332;
5195 pub const SET_OUTPUT = 9221;
5196 pub const SET_FILTER = 1074275334;
5197 pub const SET_BPF = 1074013192;
5198 pub const PAUSE_OUTPUT = 1074013193;
5199 pub const QUERY_BPF = 3221758986;
5200 pub const MODIFY_ATTRIBUTES = 1074275339;
5201 };
5202};