linux-perf-event-reader-0.10.0/.cargo_vcs_info.json0000644000000001360000000000100155620ustar { "git": { "sha1": "cee8509f83377f7d8deca57ca2d46a9ef60b6762" }, "path_in_vcs": "" }linux-perf-event-reader-0.10.0/.gitignore000064400000000000000000000000501046102023000163350ustar 00000000000000/target /fixtures .DS_Store /Cargo.lock linux-perf-event-reader-0.10.0/Cargo.toml0000644000000022670000000000100135670ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "linux-perf-event-reader" version = "0.10.0" authors = ["Markus Stange "] exclude = [ "/.github", "/.vscode", "/tests", ] description = "Parse Linux perf_event information from raw bytes" documentation = "https://docs.rs/linux-perf-event-reader/" readme = "README.md" keywords = [ "linux", "perf", "parser", ] categories = [ "development-tools::profiling", "parser-implementations", ] license = "MIT OR Apache-2.0" repository = "https://github.com/mstange/linux-perf-event-reader/" [dependencies.bitflags] version = "2" [dependencies.byteorder] version = "1.4.3" [dependencies.memchr] version = "2.4.1" [dependencies.thiserror] version = "1.0.30" linux-perf-event-reader-0.10.0/Cargo.toml.orig000064400000000000000000000011531046102023000172410ustar 00000000000000[package] name = "linux-perf-event-reader" version = "0.10.0" edition = "2021" license = "MIT OR Apache-2.0" authors = ["Markus Stange "] categories = ["development-tools::profiling", "parser-implementations"] description = "Parse Linux perf_event information from raw bytes" keywords = ["linux", "perf", "parser"] readme = "README.md" documentation = "https://docs.rs/linux-perf-event-reader/" repository = "https://github.com/mstange/linux-perf-event-reader/" exclude = ["/.github", "/.vscode", "/tests"] [dependencies] bitflags = "2" byteorder = "1.4.3" memchr = "2.4.1" thiserror = "1.0.30" linux-perf-event-reader-0.10.0/LICENSE-APACHE000064400000000000000000000251371046102023000163060ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. linux-perf-event-reader-0.10.0/LICENSE-MIT000064400000000000000000000020701046102023000160050ustar 00000000000000Copyright (c) 2018 Markus Stange Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. linux-perf-event-reader-0.10.0/README.md000064400000000000000000000042021046102023000156270ustar 00000000000000# linux-perf-event-reader This crate lets you parse Linux perf events and associated structures. ## Example ```rust use linux_perf_event_reader::{Endianness, PerfEventAttr, RawData, RecordType}; use linux_perf_event_reader::records::{CommOrExecRecord, EventRecord, RawEventRecord, RecordParseInfo}; // Read the perf_event_attr data. let attr_data = [ 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 3, 0, 0, 0, 0, 0, 0, 47, 177, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 183, 215, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 15, 255, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, ]; let (attr, _size) = PerfEventAttr::parse::<_, byteorder::LittleEndian>(&attr_data[..]).unwrap(); let parse_info = RecordParseInfo::new(&attr, Endianness::LittleEndian); let body = b"lG\x08\0lG\x08\0dump_syms\0\0\0\0\0\0\0lG\x08\0lG\x08\08\x1b\xf8\x18hX\x04\0"; let body_raw_data = RawData::from(&body[..]); let raw_record = RawEventRecord::new(RecordType::COMM, 0x2000, body_raw_data, parse_info); let parsed_record = raw_record.parse().unwrap(); assert_eq!( parsed_record, EventRecord::Comm(CommOrExecRecord { pid: 542572, tid: 542572, name: RawData::Single(b"dump_syms"), is_execve: true }) ); ``` ## Acknowledgements Some of the code in this repo was based on [**@koute**'s `not-perf` project](https://github.com/koute/not-perf/tree/20e4ddc2bf8895d96664ab839a64c36f416023c8/perf_event_open/src). ## License Licensed under either of * Apache License, Version 2.0 ([`LICENSE-APACHE`](./LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([`LICENSE-MIT`](./LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. linux-perf-event-reader-0.10.0/src/common_data.rs000064400000000000000000000136161046102023000177770ustar 00000000000000use byteorder::{BigEndian, ByteOrder, LittleEndian}; use crate::{Endianness, RawData, SampleFormat}; use super::RecordParseInfo; #[derive(Clone, Debug, Default)] pub struct CommonData { pub pid: Option, pub tid: Option, pub timestamp: Option, pub id: Option, pub stream_id: Option, pub cpu: Option, } impl CommonData { pub fn parse_sample( data: RawData, parse_info: &RecordParseInfo, ) -> Result { match parse_info.endian { Endianness::LittleEndian => Self::parse_sample_impl::(data, parse_info), Endianness::BigEndian => Self::parse_sample_impl::(data, parse_info), } } pub fn parse_sample_impl( data: RawData, parse_info: &RecordParseInfo, ) -> Result { let sample_format = parse_info.sample_format; // { u64 id; } && PERF_SAMPLE_IDENTIFIER // { u64 ip; } && PERF_SAMPLE_IP // { u32 pid, tid; } && PERF_SAMPLE_TID // { u64 time; } && PERF_SAMPLE_TIME // { u64 addr; } && PERF_SAMPLE_ADDR // { u64 id; } && PERF_SAMPLE_ID // { u64 stream_id;} && PERF_SAMPLE_STREAM_ID // { u32 cpu, res; } && PERF_SAMPLE_CPU let mut cur = data; let identifier = if sample_format.contains(SampleFormat::IDENTIFIER) { Some(cur.read_u64::()?) } else { None }; if sample_format.contains(SampleFormat::IP) { let _ip = cur.read_u64::()?; } let (pid, tid) = if sample_format.contains(SampleFormat::TID) { let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; (Some(pid), Some(tid)) } else { (None, None) }; let timestamp = if sample_format.contains(SampleFormat::TIME) { Some(cur.read_u64::()?) } else { None }; if sample_format.contains(SampleFormat::ADDR) { let _addr = cur.read_u64::()?; } let id = if sample_format.contains(SampleFormat::ID) { Some(cur.read_u64::()?) } else { None }; let id = identifier.or(id); let stream_id = if sample_format.contains(SampleFormat::STREAM_ID) { Some(cur.read_u64::()?) } else { None }; let cpu = if sample_format.contains(SampleFormat::CPU) { let cpu = cur.read_u32::()?; let _ = cur.read_u32::()?; // Reserved field; is always zero. Some(cpu) } else { None }; Ok(CommonData { pid, tid, timestamp, id, stream_id, cpu, }) } pub fn parse_nonsample( data: RawData, parse_info: &RecordParseInfo, ) -> Result { match parse_info.endian { Endianness::LittleEndian => { Self::parse_nonsample_impl::(data, parse_info) } Endianness::BigEndian => Self::parse_nonsample_impl::(data, parse_info), } } pub fn parse_nonsample_impl( data: RawData, parse_info: &RecordParseInfo, ) -> Result { if let Some(common_data_offset_from_end) = parse_info.common_data_offset_from_end { let common_data_offset_from_end = common_data_offset_from_end as usize; let sample_format = parse_info.sample_format; let mut cur = data; let common_data_offset_from_start = cur .len() .checked_sub(common_data_offset_from_end) .ok_or(std::io::ErrorKind::UnexpectedEof)?; cur.skip(common_data_offset_from_start)?; // struct sample_id { // { u32 pid, tid; } /* if PERF_SAMPLE_TID set */ // { u64 timestamp; } /* if PERF_SAMPLE_TIME set */ // { u64 id; } /* if PERF_SAMPLE_ID set */ // { u64 stream_id; } /* if PERF_SAMPLE_STREAM_ID set */ // { u32 cpu, res; } /* if PERF_SAMPLE_CPU set */ // { u64 identifier;} /* if PERF_SAMPLE_IDENTIFIER set */ // }; let (pid, tid) = if sample_format.contains(SampleFormat::TID) { let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; (Some(pid), Some(tid)) } else { (None, None) }; let timestamp = if sample_format.contains(SampleFormat::TIME) { Some(cur.read_u64::()?) } else { None }; let id = if sample_format.contains(SampleFormat::ID) { Some(cur.read_u64::()?) } else { None }; let stream_id = if sample_format.contains(SampleFormat::STREAM_ID) { Some(cur.read_u64::()?) } else { None }; let cpu = if sample_format.contains(SampleFormat::CPU) { let cpu = cur.read_u32::()?; let _ = cur.read_u32::()?; // Reserved field; is always zero. Some(cpu) } else { None }; let identifier = if sample_format.contains(SampleFormat::IDENTIFIER) { Some(cur.read_u64::()?) } else { None }; let id = identifier.or(id); Ok(CommonData { pid, tid, timestamp, id, stream_id, cpu, }) } else { Ok(Default::default()) } } } linux-perf-event-reader-0.10.0/src/constants.rs000064400000000000000000000716131046102023000175330ustar 00000000000000/// Constants from perf_event.h /// A hardware perf event. /// /// Config format: 0xEEEEEEEE000000AA /// /// - AA: hardware event ID /// - EEEEEEEE: PMU type ID pub const PERF_TYPE_HARDWARE: u32 = 0; /// A software perf event. /// /// 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). pub const PERF_TYPE_SOFTWARE: u32 = 1; /// A tracepoint perf event. pub const PERF_TYPE_TRACEPOINT: u32 = 2; /// A hardware cache perf event. /// /// The corresponding `attr.config` chooses the cache, the cache op, /// and the cache op result. /// /// Config format: 0xEEEEEEEE00DDCCBB /// /// - BB: hardware cache ID /// - CC: hardware cache op ID /// - DD: hardware cache op result ID /// - EEEEEEEE: PMU type ID /// /// ```plain /// { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x /// { read, write, prefetch } x /// { accesses, misses } /// ``` pub const PERF_TYPE_HW_CACHE: u32 = 3; /// A raw perf event. pub const PERF_TYPE_RAW: u32 = 4; /// A breakpoint perf event. pub const PERF_TYPE_BREAKPOINT: u32 = 5; pub const PERF_COUNT_HW_CPU_CYCLES: u8 = 0; pub const PERF_COUNT_HW_INSTRUCTIONS: u8 = 1; pub const PERF_COUNT_HW_CACHE_REFERENCES: u8 = 2; pub const PERF_COUNT_HW_CACHE_MISSES: u8 = 3; pub const PERF_COUNT_HW_BRANCH_INSTRUCTIONS: u8 = 4; pub const PERF_COUNT_HW_BRANCH_MISSES: u8 = 5; pub const PERF_COUNT_HW_BUS_CYCLES: u8 = 6; pub const PERF_COUNT_HW_STALLED_CYCLES_FRONTEND: u8 = 7; pub const PERF_COUNT_HW_STALLED_CYCLES_BACKEND: u8 = 8; pub const PERF_COUNT_HW_REF_CPU_CYCLES: u8 = 9; pub const PERF_COUNT_SW_CPU_CLOCK: u64 = 0; pub const PERF_COUNT_SW_TASK_CLOCK: u64 = 1; pub const PERF_COUNT_SW_PAGE_FAULTS: u64 = 2; pub const PERF_COUNT_SW_CONTEXT_SWITCHES: u64 = 3; pub const PERF_COUNT_SW_CPU_MIGRATIONS: u64 = 4; pub const PERF_COUNT_SW_PAGE_FAULTS_MIN: u64 = 5; pub const PERF_COUNT_SW_PAGE_FAULTS_MAJ: u64 = 6; pub const PERF_COUNT_SW_ALIGNMENT_FAULTS: u64 = 7; pub const PERF_COUNT_SW_EMULATION_FAULTS: u64 = 8; pub const PERF_COUNT_SW_DUMMY: u64 = 9; pub const PERF_COUNT_SW_BPF_OUTPUT: u64 = 10; pub const PERF_COUNT_SW_CGROUP_SWITCHES: u64 = 11; pub const PERF_COUNT_HW_CACHE_L1D: u8 = 0; pub const PERF_COUNT_HW_CACHE_L1I: u8 = 1; pub const PERF_COUNT_HW_CACHE_LL: u8 = 2; pub const PERF_COUNT_HW_CACHE_DTLB: u8 = 3; pub const PERF_COUNT_HW_CACHE_ITLB: u8 = 4; pub const PERF_COUNT_HW_CACHE_BPU: u8 = 5; pub const PERF_COUNT_HW_CACHE_NODE: u8 = 6; pub const PERF_COUNT_HW_CACHE_OP_READ: u8 = 0; pub const PERF_COUNT_HW_CACHE_OP_WRITE: u8 = 1; pub const PERF_COUNT_HW_CACHE_OP_PREFETCH: u8 = 2; pub const PERF_COUNT_HW_CACHE_RESULT_ACCESS: u8 = 0; pub const PERF_COUNT_HW_CACHE_RESULT_MISS: u8 = 1; pub const HW_BREAKPOINT_EMPTY: u8 = 0; pub const HW_BREAKPOINT_R: u8 = 1; pub const HW_BREAKPOINT_W: u8 = 2; pub const HW_BREAKPOINT_RW: u8 = HW_BREAKPOINT_R | HW_BREAKPOINT_W; pub const HW_BREAKPOINT_X: u8 = 4; pub const HW_BREAKPOINT_INVALID: u8 = HW_BREAKPOINT_RW | HW_BREAKPOINT_X; /// sizeof first published struct pub const PERF_ATTR_SIZE_VER0: u32 = 64; /// add: config2 pub const PERF_ATTR_SIZE_VER1: u32 = 72; /// add: branch_sample_type pub const PERF_ATTR_SIZE_VER2: u32 = 80; /// add: sample_regs_user, sample_stack_user, clockid pub const PERF_ATTR_SIZE_VER3: u32 = 96; /// add: sample_regs_intr pub const PERF_ATTR_SIZE_VER4: u32 = 104; /// add: aux_watermark pub const PERF_ATTR_SIZE_VER5: u32 = 112; /// add: aux_sample_size pub const PERF_ATTR_SIZE_VER6: u32 = 120; /// add: sig_data pub const PERF_ATTR_SIZE_VER7: u32 = 128; /// off by default pub const ATTR_FLAG_BIT_DISABLED: u64 = 1 << 0; /// children inherit it pub const ATTR_FLAG_BIT_INHERIT: u64 = 1 << 1; /// must always be on PMU pub const ATTR_FLAG_BIT_PINNED: u64 = 1 << 2; /// only group on PMU pub const ATTR_FLAG_BIT_EXCLUSIVE: u64 = 1 << 3; /// don't count user pub const ATTR_FLAG_BIT_EXCLUDE_USER: u64 = 1 << 4; /// don't count kernel pub const ATTR_FLAG_BIT_EXCLUDE_KERNEL: u64 = 1 << 5; /// don't count hypervisor pub const ATTR_FLAG_BIT_EXCLUDE_HV: u64 = 1 << 6; /// don't count when idle pub const ATTR_FLAG_BIT_EXCLUDE_IDLE: u64 = 1 << 7; /// include mmap data pub const ATTR_FLAG_BIT_MMAP: u64 = 1 << 8; /// include comm data pub const ATTR_FLAG_BIT_COMM: u64 = 1 << 9; /// use freq, not period pub const ATTR_FLAG_BIT_FREQ: u64 = 1 << 10; /// per task counts pub const ATTR_FLAG_BIT_INHERIT_STAT: u64 = 1 << 11; /// next exec enables pub const ATTR_FLAG_BIT_ENABLE_ON_EXEC: u64 = 1 << 12; /// trace fork/exit pub const ATTR_FLAG_BIT_TASK: u64 = 1 << 13; /// wakeup_watermark pub const ATTR_FLAG_BIT_WATERMARK: u64 = 1 << 14; /// skid constraint /// Specifies how precise the instruction address should be. /// /// From the perf-list man page: /// /// > 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, or uses randomization to avoid /// > sample shadowing effects. /// > /// > For Intel systems precise event sampling is implemented with PEBS /// > which supports up to precise-level 2, and precise level 3 for /// > some special cases. /// > /// > On AMD systems it is implemented using IBS (up to precise-level /// > 2). The precise modifier works with event types 0x76 (cpu-cycles, /// > CPU clocks not halted) and 0xC1 (micro-ops retired). Both events /// > map to IBS execution sampling (IBS op) with the IBS Op Counter /// > Control bit (IbsOpCntCtl) set respectively (see AMD64 /// > Architecture Programmer’s Manual Volume 2: System Programming, /// > 13.3 Instruction-Based Sampling). Examples to use IBS: /// > /// > perf record -a -e cpu-cycles:p ... # use ibs op counting cycles /// > perf record -a -e r076:p ... # same as -e cpu-cycles:p /// > perf record -a -e r0C1:p ... # use ibs op counting micro-ops /// /// From Brendan Gregg's perf page: /// /// > perf can use precise sampling by adding a :p modifier to the PMC event /// > name, eg, "-e instructions:p". The more p's, the more accurate. /// pub const ATTR_FLAG_BITMASK_PRECISE_IP: u64 = 1 << 15 | 1 << 16; /// non-exec mmap data pub const ATTR_FLAG_BIT_MMAP_DATA: u64 = 1 << 17; /// sample_type all events pub const ATTR_FLAG_BIT_SAMPLE_ID_ALL: u64 = 1 << 18; /// don't count in host pub const ATTR_FLAG_BIT_EXCLUDE_HOST: u64 = 1 << 19; /// don't count in guest pub const ATTR_FLAG_BIT_EXCLUDE_GUEST: u64 = 1 << 20; /// exclude kernel callchains pub const ATTR_FLAG_BIT_EXCLUDE_CALLCHAIN_KERNEL: u64 = 1 << 21; /// exclude user callchains pub const ATTR_FLAG_BIT_EXCLUDE_CALLCHAIN_USER: u64 = 1 << 22; /// include mmap with inode data pub const ATTR_FLAG_BIT_MMAP2: u64 = 1 << 23; /// flag comm events that are due to exec pub const ATTR_FLAG_BIT_COMM_EXEC: u64 = 1 << 24; /// use @clockid for time fields pub const ATTR_FLAG_BIT_USE_CLOCKID: u64 = 1 << 25; /// context switch data pub const ATTR_FLAG_BIT_CONTEXT_SWITCH: u64 = 1 << 26; /// Write ring buffer from end to beginning pub const ATTR_FLAG_BIT_WRITE_BACKWARD: u64 = 1 << 27; /// include namespaces data pub const ATTR_FLAG_BIT_NAMESPACES: u64 = 1 << 28; /// include ksymbol events pub const ATTR_FLAG_BIT_KSYMBOL: u64 = 1 << 29; /// include bpf events pub const ATTR_FLAG_BIT_BPF_EVENT: u64 = 1 << 30; /// generate AUX records instead of events pub const ATTR_FLAG_BIT_AUX_OUTPUT: u64 = 1 << 31; /// include cgroup events pub const ATTR_FLAG_BIT_CGROUP: u64 = 1 << 32; /// include text poke events pub const ATTR_FLAG_BIT_TEXT_POKE: u64 = 1 << 33; /// use build id in mmap2 events pub const ATTR_FLAG_BIT_BUILD_ID: u64 = 1 << 34; /// children only inherit if cloned with CLONE_THREAD pub const ATTR_FLAG_BIT_INHERIT_THREAD: u64 = 1 << 35; /// event is removed from task on exec pub const ATTR_FLAG_BIT_REMOVE_ON_EXEC: u64 = 1 << 36; /// send synchronous SIGTRAP on event pub const ATTR_FLAG_BIT_SIGTRAP: u64 = 1 << 37; /* * If perf_event_attr.sample_id_all is set then all event types will * have the sample_type selected fields related to where/when * (identity) an event took place (TID, TIME, ID, STREAM_ID, CPU, * IDENTIFIER) described in PERF_RECORD_SAMPLE below, it will be stashed * just after the perf_event_header and the fields already present for * the existing fields, i.e. at the end of the payload. That way a newer * perf.data file will be supported by older perf tools, with these new * optional fields being ignored. * * struct sample_id { * { u32 pid, tid; } && PERF_SAMPLE_TID * { u64 time; } && PERF_SAMPLE_TIME * { u64 id; } && PERF_SAMPLE_ID * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID * { u32 cpu, res; } && PERF_SAMPLE_CPU * { u64 id; } && PERF_SAMPLE_IDENTIFIER * } && perf_event_attr::sample_id_all * * Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. The * advantage of PERF_SAMPLE_IDENTIFIER is that its position is fixed * relative to header.size. */ /* * The MMAP events record the PROT_EXEC mappings so that we can * correlate userspace IPs to code. They have the following structure: * * struct { * struct perf_event_header header; * * u32 pid, tid; * u64 addr; * u64 len; * u64 pgoff; * char filename[]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_MMAP: u32 = 1; /* * struct { * struct perf_event_header header; * u64 id; * u64 lost; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_LOST: u32 = 2; /* * struct { * struct perf_event_header header; * * u32 pid, tid; * char comm[]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_COMM: u32 = 3; /* * struct { * struct perf_event_header header; * u32 pid, ppid; * u32 tid, ptid; * u64 time; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_EXIT: u32 = 4; /* * struct { * struct perf_event_header header; * u64 time; * u64 id; * u64 stream_id; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_THROTTLE: u32 = 5; pub const PERF_RECORD_UNTHROTTLE: u32 = 6; /* * struct { * struct perf_event_header header; * u32 pid, ppid; * u32 tid, ptid; * u64 time; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_FORK: u32 = 7; /* * struct { * struct perf_event_header header; * u32 pid, tid; * * struct read_format values; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_READ: u32 = 8; /* * struct { * struct perf_event_header header; * * # * # Note that PERF_SAMPLE_IDENTIFIER duplicates PERF_SAMPLE_ID. * # The advantage of PERF_SAMPLE_IDENTIFIER is that its position * # is fixed relative to header. * # * * { u64 id; } && PERF_SAMPLE_IDENTIFIER * { u64 ip; } && PERF_SAMPLE_IP * { u32 pid, tid; } && PERF_SAMPLE_TID * { u64 time; } && PERF_SAMPLE_TIME * { u64 addr; } && PERF_SAMPLE_ADDR * { u64 id; } && PERF_SAMPLE_ID * { u64 stream_id;} && PERF_SAMPLE_STREAM_ID * { u32 cpu, res; } && PERF_SAMPLE_CPU * { u64 period; } && PERF_SAMPLE_PERIOD * * { struct read_format values; } && PERF_SAMPLE_READ * * # * # The callchain includes both regular addresses, and special "context" * # frames. The context frames are >= PERF_CONTEXT_MAX and annotate the * # subsequent addresses as user / kernel / hypervisor / guest addresses. * # * * { u64 nr, * u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN * * # * # The RAW record below is opaque data wrt the ABI * # * # That is, the ABI doesn't make any promises wrt to * # the stability of its content, it may vary depending * # on event, hardware, kernel version and phase of * # the moon. * # * # In other words, PERF_SAMPLE_RAW contents are not an ABI. * # * * { u32 size; * char data[size];}&& PERF_SAMPLE_RAW * * { u64 nr; * { u64 hw_idx; } && PERF_SAMPLE_BRANCH_HW_INDEX * { u64 from, to, flags } lbr[nr]; * } && PERF_SAMPLE_BRANCH_STACK * * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_USER * * { u64 size; * char data[size]; * u64 dyn_size; } && PERF_SAMPLE_STACK_USER * * { union perf_sample_weight * { * u64 full; && PERF_SAMPLE_WEIGHT * #if defined(__LITTLE_ENDIAN_BITFIELD) * struct { * u32 var1_dw; * u16 var2_w; * u16 var3_w; * } && PERF_SAMPLE_WEIGHT_STRUCT * #elif defined(__BIG_ENDIAN_BITFIELD) * struct { * u16 var3_w; * u16 var2_w; * u32 var1_dw; * } && PERF_SAMPLE_WEIGHT_STRUCT * #endif * } * } * { u64 data_src; } && PERF_SAMPLE_DATA_SRC * { u64 transaction; } && PERF_SAMPLE_TRANSACTION * { u64 abi; # enum perf_sample_regs_abi * u64 regs[weight(mask)]; } && PERF_SAMPLE_REGS_INTR * { u64 phys_addr;} && PERF_SAMPLE_PHYS_ADDR * { u64 size; * char data[size]; } && PERF_SAMPLE_AUX * { u64 data_page_size;} && PERF_SAMPLE_DATA_PAGE_SIZE * { u64 code_page_size;} && PERF_SAMPLE_CODE_PAGE_SIZE * }; */ pub const PERF_RECORD_SAMPLE: u32 = 9; /* * The MMAP2 records are an augmented version of MMAP, they add * maj, min, ino numbers to be used to uniquely identify each mapping * * struct { * struct perf_event_header header; * * u32 pid, tid; * u64 addr; * u64 len; * u64 pgoff; * union { * struct { * u32 maj; * u32 min; * u64 ino; * u64 ino_generation; * }; * struct { * u8 build_id_size; * u8 __reserved_1; * u16 __reserved_2; * u8 build_id[20]; * }; * }; * u32 prot, flags; * char filename[]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_MMAP2: u32 = 10; /* * Records that new data landed in the AUX buffer part. * * struct { * struct perf_event_header header; * * u64 aux_offset; * u64 aux_size; * u64 flags; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_AUX: u32 = 11; /* * Indicates that instruction trace has started * * struct { * struct perf_event_header header; * u32 pid; * u32 tid; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_ITRACE_START: u32 = 12; /* * Records the dropped/lost sample number. * * struct { * struct perf_event_header header; * * u64 lost; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_LOST_SAMPLES: u32 = 13; /* * Records a context switch in or out (flagged by * PERF_RECORD_MISC_SWITCH_OUT). See also * PERF_RECORD_SWITCH_CPU_WIDE. * * struct { * struct perf_event_header header; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_SWITCH: u32 = 14; /* * CPU-wide version of PERF_RECORD_SWITCH with next_prev_pid and * next_prev_tid that are the next (switching out) or previous * (switching in) pid/tid. * * struct { * struct perf_event_header header; * u32 next_prev_pid; * u32 next_prev_tid; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_SWITCH_CPU_WIDE: u32 = 15; /* * struct { * struct perf_event_header header; * u32 pid; * u32 tid; * u64 nr_namespaces; * { u64 dev, inode; } [nr_namespaces]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_NAMESPACES: u32 = 16; /* * Record ksymbol register/unregister events: * * struct { * struct perf_event_header header; * u64 addr; * u32 len; * u16 ksym_type; * u16 flags; * char name[]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_KSYMBOL: u32 = 17; /* * Record bpf events: * enum perf_bpf_event_type { * PERF_BPF_EVENT_UNKNOWN = 0, * PERF_BPF_EVENT_PROG_LOAD = 1, * PERF_BPF_EVENT_PROG_UNLOAD = 2, * }; * * struct { * struct perf_event_header header; * u16 type; * u16 flags; * u32 id; * u8 tag[BPF_TAG_SIZE]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_BPF_EVENT: u32 = 18; /* * struct { * struct perf_event_header header; * u64 id; * char path[]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_CGROUP: u32 = 19; /* * Records changes to kernel text i.e. self-modified code. 'old_len' is * the number of old bytes, 'new_len' is the number of new bytes. Either * 'old_len' or 'new_len' may be zero to indicate, for example, the * addition or removal of a trampoline. 'bytes' contains the old bytes * followed immediately by the new bytes. * * struct { * struct perf_event_header header; * u64 addr; * u16 old_len; * u16 new_len; * u8 bytes[]; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_TEXT_POKE: u32 = 20; /* * Data written to the AUX area by hardware due to aux_output, may need * to be matched to the event by an architecture-specific hardware ID. * This records the hardware ID, but requires sample_id to provide the * event ID. e.g. Intel PT uses this record to disambiguate PEBS-via-PT * records from multiple events. * * struct { * struct perf_event_header header; * u64 hw_id; * struct sample_id sample_id; * }; */ pub const PERF_RECORD_AUX_OUTPUT_HW_ID: u32 = 21; pub const PERF_RECORD_USER_TYPE_START: u32 = 64; pub const PERF_SAMPLE_IP: u64 = 1 << 0; pub const PERF_SAMPLE_TID: u64 = 1 << 1; pub const PERF_SAMPLE_TIME: u64 = 1 << 2; pub const PERF_SAMPLE_ADDR: u64 = 1 << 3; pub const PERF_SAMPLE_READ: u64 = 1 << 4; pub const PERF_SAMPLE_CALLCHAIN: u64 = 1 << 5; pub const PERF_SAMPLE_ID: u64 = 1 << 6; pub const PERF_SAMPLE_CPU: u64 = 1 << 7; pub const PERF_SAMPLE_PERIOD: u64 = 1 << 8; pub const PERF_SAMPLE_STREAM_ID: u64 = 1 << 9; pub const PERF_SAMPLE_RAW: u64 = 1 << 10; pub const PERF_SAMPLE_BRANCH_STACK: u64 = 1 << 11; pub const PERF_SAMPLE_REGS_USER: u64 = 1 << 12; pub const PERF_SAMPLE_STACK_USER: u64 = 1 << 13; pub const PERF_SAMPLE_WEIGHT: u64 = 1 << 14; pub const PERF_SAMPLE_DATA_SRC: u64 = 1 << 15; pub const PERF_SAMPLE_IDENTIFIER: u64 = 1 << 16; pub const PERF_SAMPLE_TRANSACTION: u64 = 1 << 17; pub const PERF_SAMPLE_REGS_INTR: u64 = 1 << 18; pub const PERF_SAMPLE_PHYS_ADDR: u64 = 1 << 19; pub const PERF_SAMPLE_AUX: u64 = 1 << 20; pub const PERF_SAMPLE_CGROUP: u64 = 1 << 21; pub const PERF_SAMPLE_DATA_PAGE_SIZE: u64 = 1 << 22; pub const PERF_SAMPLE_CODE_PAGE_SIZE: u64 = 1 << 23; pub const PERF_SAMPLE_WEIGHT_STRUCT: u64 = 1 << 24; pub const PERF_REG_X86_AX: u64 = 0; pub const PERF_REG_X86_BX: u64 = 1; pub const PERF_REG_X86_CX: u64 = 2; pub const PERF_REG_X86_DX: u64 = 3; pub const PERF_REG_X86_SI: u64 = 4; pub const PERF_REG_X86_DI: u64 = 5; pub const PERF_REG_X86_BP: u64 = 6; pub const PERF_REG_X86_SP: u64 = 7; pub const PERF_REG_X86_IP: u64 = 8; pub const PERF_REG_X86_FLAGS: u64 = 9; pub const PERF_REG_X86_CS: u64 = 10; pub const PERF_REG_X86_SS: u64 = 11; pub const PERF_REG_X86_DS: u64 = 12; pub const PERF_REG_X86_ES: u64 = 13; pub const PERF_REG_X86_FS: u64 = 14; pub const PERF_REG_X86_GS: u64 = 15; pub const PERF_REG_X86_R8: u64 = 16; pub const PERF_REG_X86_R9: u64 = 17; pub const PERF_REG_X86_R10: u64 = 18; pub const PERF_REG_X86_R11: u64 = 19; pub const PERF_REG_X86_R12: u64 = 20; pub const PERF_REG_X86_R13: u64 = 21; pub const PERF_REG_X86_R14: u64 = 22; pub const PERF_REG_X86_R15: u64 = 23; pub const PERF_REG_X86_32_MAX: u64 = PERF_REG_X86_GS + 1; pub const PERF_REG_X86_64_MAX: u64 = PERF_REG_X86_R15 + 1; pub const PERF_REG_ARM_R0: u64 = 0; pub const PERF_REG_ARM_R1: u64 = 1; pub const PERF_REG_ARM_R2: u64 = 2; pub const PERF_REG_ARM_R3: u64 = 3; pub const PERF_REG_ARM_R4: u64 = 4; pub const PERF_REG_ARM_R5: u64 = 5; pub const PERF_REG_ARM_R6: u64 = 6; pub const PERF_REG_ARM_R7: u64 = 7; pub const PERF_REG_ARM_R8: u64 = 8; pub const PERF_REG_ARM_R9: u64 = 9; pub const PERF_REG_ARM_R10: u64 = 10; pub const PERF_REG_ARM_FP: u64 = 11; pub const PERF_REG_ARM_IP: u64 = 12; pub const PERF_REG_ARM_SP: u64 = 13; pub const PERF_REG_ARM_LR: u64 = 14; pub const PERF_REG_ARM_PC: u64 = 15; pub const PERF_REG_ARM_MAX: u64 = 16; pub const PERF_REG_MIPS_PC: u64 = 0; pub const PERF_REG_MIPS_R1: u64 = 1; pub const PERF_REG_MIPS_R2: u64 = 2; pub const PERF_REG_MIPS_R3: u64 = 3; pub const PERF_REG_MIPS_R4: u64 = 4; pub const PERF_REG_MIPS_R5: u64 = 5; pub const PERF_REG_MIPS_R6: u64 = 6; pub const PERF_REG_MIPS_R7: u64 = 7; pub const PERF_REG_MIPS_R8: u64 = 8; pub const PERF_REG_MIPS_R9: u64 = 9; pub const PERF_REG_MIPS_R10: u64 = 10; pub const PERF_REG_MIPS_R11: u64 = 11; pub const PERF_REG_MIPS_R12: u64 = 12; pub const PERF_REG_MIPS_R13: u64 = 13; pub const PERF_REG_MIPS_R14: u64 = 14; pub const PERF_REG_MIPS_R15: u64 = 15; pub const PERF_REG_MIPS_R16: u64 = 16; pub const PERF_REG_MIPS_R17: u64 = 17; pub const PERF_REG_MIPS_R18: u64 = 18; pub const PERF_REG_MIPS_R19: u64 = 19; pub const PERF_REG_MIPS_R20: u64 = 20; pub const PERF_REG_MIPS_R21: u64 = 21; pub const PERF_REG_MIPS_R22: u64 = 22; pub const PERF_REG_MIPS_R23: u64 = 23; pub const PERF_REG_MIPS_R24: u64 = 24; pub const PERF_REG_MIPS_R25: u64 = 25; pub const PERF_REG_MIPS_R28: u64 = 26; pub const PERF_REG_MIPS_R29: u64 = 27; pub const PERF_REG_MIPS_R30: u64 = 28; pub const PERF_REG_MIPS_R31: u64 = 29; pub const PERF_REG_MIPS_MAX: u64 = PERF_REG_MIPS_R31 + 1; pub const PERF_REG_ARM64_X0: u64 = 0; pub const PERF_REG_ARM64_X1: u64 = 1; pub const PERF_REG_ARM64_X2: u64 = 2; pub const PERF_REG_ARM64_X3: u64 = 3; pub const PERF_REG_ARM64_X4: u64 = 4; pub const PERF_REG_ARM64_X5: u64 = 5; pub const PERF_REG_ARM64_X6: u64 = 6; pub const PERF_REG_ARM64_X7: u64 = 7; pub const PERF_REG_ARM64_X8: u64 = 8; pub const PERF_REG_ARM64_X9: u64 = 9; pub const PERF_REG_ARM64_X10: u64 = 10; pub const PERF_REG_ARM64_X11: u64 = 11; pub const PERF_REG_ARM64_X12: u64 = 12; pub const PERF_REG_ARM64_X13: u64 = 13; pub const PERF_REG_ARM64_X14: u64 = 14; pub const PERF_REG_ARM64_X15: u64 = 15; pub const PERF_REG_ARM64_X16: u64 = 16; pub const PERF_REG_ARM64_X17: u64 = 17; pub const PERF_REG_ARM64_X18: u64 = 18; pub const PERF_REG_ARM64_X19: u64 = 19; pub const PERF_REG_ARM64_X20: u64 = 20; pub const PERF_REG_ARM64_X21: u64 = 21; pub const PERF_REG_ARM64_X22: u64 = 22; pub const PERF_REG_ARM64_X23: u64 = 23; pub const PERF_REG_ARM64_X24: u64 = 24; pub const PERF_REG_ARM64_X25: u64 = 25; pub const PERF_REG_ARM64_X26: u64 = 26; pub const PERF_REG_ARM64_X27: u64 = 27; pub const PERF_REG_ARM64_X28: u64 = 28; pub const PERF_REG_ARM64_X29: u64 = 29; pub const PERF_REG_ARM64_LR: u64 = 30; pub const PERF_REG_ARM64_SP: u64 = 31; pub const PERF_REG_ARM64_PC: u64 = 32; pub const PERF_REG_ARM64_MAX: u64 = 33; pub const PERF_SAMPLE_REGS_ABI_32: u64 = 1; pub const PERF_SAMPLE_REGS_ABI_64: u64 = 2; pub const PERF_FORMAT_TOTAL_TIME_ENABLED: u64 = 1 << 0; pub const PERF_FORMAT_TOTAL_TIME_RUNNING: u64 = 1 << 1; pub const PERF_FORMAT_ID: u64 = 1 << 2; pub const PERF_FORMAT_GROUP: u64 = 1 << 3; /* * 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. */ /// user branches pub const PERF_SAMPLE_BRANCH_USER_SHIFT: u32 = 0; /// kernel branches pub const PERF_SAMPLE_BRANCH_KERNEL_SHIFT: u32 = 1; /// hypervisor branches pub const PERF_SAMPLE_BRANCH_HV_SHIFT: u32 = 2; /// any branch types pub const PERF_SAMPLE_BRANCH_ANY_SHIFT: u32 = 3; /// any call branch pub const PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT: u32 = 4; /// any return branch pub const PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT: u32 = 5; /// indirect calls pub const PERF_SAMPLE_BRANCH_IND_CALL_SHIFT: u32 = 6; /// transaction aborts pub const PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT: u32 = 7; /// in transaction pub const PERF_SAMPLE_BRANCH_IN_TX_SHIFT: u32 = 8; /// not in transaction pub const PERF_SAMPLE_BRANCH_NO_TX_SHIFT: u32 = 9; /// conditional branches pub const PERF_SAMPLE_BRANCH_COND_SHIFT: u32 = 10; /// call/ret stack pub const PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT: u32 = 11; /// indirect jumps pub const PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT: u32 = 12; /// direct call pub const PERF_SAMPLE_BRANCH_CALL_SHIFT: u32 = 13; /// no flags pub const PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT: u32 = 14; /// no cycles pub const PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT: u32 = 15; /// save branch type pub const PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT: u32 = 16; /// save low level index of raw branch records pub const PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT: u32 = 17; pub const PERF_SAMPLE_BRANCH_USER: u64 = 1 << PERF_SAMPLE_BRANCH_USER_SHIFT; pub const PERF_SAMPLE_BRANCH_KERNEL: u64 = 1 << PERF_SAMPLE_BRANCH_KERNEL_SHIFT; pub const PERF_SAMPLE_BRANCH_HV: u64 = 1 << PERF_SAMPLE_BRANCH_HV_SHIFT; pub const PERF_SAMPLE_BRANCH_ANY: u64 = 1 << PERF_SAMPLE_BRANCH_ANY_SHIFT; pub const PERF_SAMPLE_BRANCH_ANY_CALL: u64 = 1 << PERF_SAMPLE_BRANCH_ANY_CALL_SHIFT; pub const PERF_SAMPLE_BRANCH_ANY_RETURN: u64 = 1 << PERF_SAMPLE_BRANCH_ANY_RETURN_SHIFT; pub const PERF_SAMPLE_BRANCH_IND_CALL: u64 = 1 << PERF_SAMPLE_BRANCH_IND_CALL_SHIFT; pub const PERF_SAMPLE_BRANCH_ABORT_TX: u64 = 1 << PERF_SAMPLE_BRANCH_ABORT_TX_SHIFT; pub const PERF_SAMPLE_BRANCH_IN_TX: u64 = 1 << PERF_SAMPLE_BRANCH_IN_TX_SHIFT; pub const PERF_SAMPLE_BRANCH_NO_TX: u64 = 1 << PERF_SAMPLE_BRANCH_NO_TX_SHIFT; pub const PERF_SAMPLE_BRANCH_COND: u64 = 1 << PERF_SAMPLE_BRANCH_COND_SHIFT; pub const PERF_SAMPLE_BRANCH_CALL_STACK: u64 = 1 << PERF_SAMPLE_BRANCH_CALL_STACK_SHIFT; pub const PERF_SAMPLE_BRANCH_IND_JUMP: u64 = 1 << PERF_SAMPLE_BRANCH_IND_JUMP_SHIFT; pub const PERF_SAMPLE_BRANCH_CALL: u64 = 1 << PERF_SAMPLE_BRANCH_CALL_SHIFT; pub const PERF_SAMPLE_BRANCH_NO_FLAGS: u64 = 1 << PERF_SAMPLE_BRANCH_NO_FLAGS_SHIFT; pub const PERF_SAMPLE_BRANCH_NO_CYCLES: u64 = 1 << PERF_SAMPLE_BRANCH_NO_CYCLES_SHIFT; pub const PERF_SAMPLE_BRANCH_TYPE_SAVE: u64 = 1 << PERF_SAMPLE_BRANCH_TYPE_SAVE_SHIFT; pub const PERF_SAMPLE_BRANCH_HW_INDEX: u64 = 1 << PERF_SAMPLE_BRANCH_HW_INDEX_SHIFT; // The current state of perf_event_header::misc bits usage: // ('|' used bit, '-' unused bit) // // 012 CDEF // |||---------|||| // // Where: // 0-2 CPUMODE_MASK // // C PROC_MAP_PARSE_TIMEOUT // D MMAP_DATA / COMM_EXEC / FORK_EXEC / SWITCH_OUT // E MMAP_BUILD_ID / EXACT_IP / SCHED_OUT_PREEMPT // F (reserved) pub const PERF_RECORD_MISC_CPUMODE_MASK: u16 = 0b111; pub const PERF_RECORD_MISC_CPUMODE_UNKNOWN: u16 = 0; pub const PERF_RECORD_MISC_KERNEL: u16 = 1; pub const PERF_RECORD_MISC_USER: u16 = 2; pub const PERF_RECORD_MISC_HYPERVISOR: u16 = 3; pub const PERF_RECORD_MISC_GUEST_KERNEL: u16 = 4; pub const PERF_RECORD_MISC_GUEST_USER: u16 = 5; /// Indicates that /proc/PID/maps parsing are truncated by time out. pub const PERF_RECORD_MISC_PROC_MAP_PARSE_TIMEOUT: u16 = 1 << 12; // The following PERF_RECORD_MISC_* are used on different // events, so can reuse the same bit position. /// Used on PERF_RECORD_MMAP events to indicate mappings which are not executable. /// Not used on PERF_RECORD_MMAP2 events - those have the full protection bitset. pub const PERF_RECORD_MISC_MMAP_DATA: u16 = 1 << 13; /// Used on PERF_RECORD_COMM event. pub const PERF_RECORD_MISC_COMM_EXEC: u16 = 1 << 13; /// Used on PERF_RECORD_FORK events (perf internal). pub const PERF_RECORD_MISC_FORK_EXEC: u16 = 1 << 13; /// Used on PERF_RECORD_SWITCH* events. pub const PERF_RECORD_MISC_SWITCH_OUT: u16 = 1 << 13; /// Indicates that the content of PERF_SAMPLE_IP points to /// the actual instruction that triggered the event. See also /// perf_event_attr::precise_ip. /// Used on PERF_RECORD_SAMPLE of precise events. pub const PERF_RECORD_MISC_EXACT_IP: u16 = 1 << 14; /// Indicates that thread was preempted in TASK_RUNNING state. /// Used on PERF_RECORD_SWITCH* events. /// /// This helps understanding whether a workload is CPU or IO bound. pub const PERF_RECORD_MISC_SWITCH_OUT_PREEMPT: u16 = 1 << 14; /// Indicates that mmap2 event carries build id data. /// Used on PERF_RECORD_MMAP2 events. pub const PERF_RECORD_MISC_MMAP_BUILD_ID: u16 = 1 << 14; /// Used in header.misc of the HEADER_BUILD_ID event. If set, the length /// of the buildid is specified in the event (no more than 20). pub const PERF_RECORD_MISC_BUILD_ID_SIZE: u16 = 1 << 15; // These PERF_CONTEXT addresses are inserted into callchain to mark the // "context" of the call chain addresses that follow. The special frames // can be differentiated from real addresses by the fact that they are // >= PERF_CONTEXT_MAX. /// The callchain frames following this context marker frame are "hypervisor" frames. pub const PERF_CONTEXT_HV: u64 = -32i64 as u64; /// The callchain frames following this context marker frame are "kernel" frames. pub const PERF_CONTEXT_KERNEL: u64 = -128i64 as u64; /// The callchain frames following this context marker frame are "user" frames. pub const PERF_CONTEXT_USER: u64 = -512i64 as u64; /// The callchain frames following this context marker frame are "guest" frames. pub const PERF_CONTEXT_GUEST: u64 = -2048i64 as u64; /// The callchain frames following this context marker frame are "guest kernel" frames. pub const PERF_CONTEXT_GUEST_KERNEL: u64 = -2176i64 as u64; /// The callchain frames following this context marker frame are "guest user" frames. pub const PERF_CONTEXT_GUEST_USER: u64 = -2560i64 as u64; /// Any callchain frames which are >= PERF_CONTEXT_MAX are not real addresses; /// instead, they mark the context of the subsequent callchain frames. pub const PERF_CONTEXT_MAX: u64 = -4095i64 as u64; linux-perf-event-reader-0.10.0/src/endian.rs000064400000000000000000000005101046102023000167410ustar 00000000000000/// An enum for little or big endian. #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Endianness { LittleEndian, BigEndian, } impl Endianness { #[cfg(target_endian = "little")] pub const NATIVE: Self = Self::LittleEndian; #[cfg(target_endian = "big")] pub const NATIVE: Self = Self::BigEndian; } linux-perf-event-reader-0.10.0/src/event_record.rs000064400000000000000000000454551046102023000202030ustar 00000000000000use crate::raw_data::RawData; use crate::utils::HexValue; use crate::{ constants, CommonData, CpuMode, Endianness, RecordIdParseInfo, RecordParseInfo, RecordType, SampleRecord, }; use byteorder::{BigEndian, ByteOrder, LittleEndian}; use std::fmt; /// Get the ID from an event record, if the sample format includes SampleFormat::IDENTIFIER. /// /// This can be used if it is not known which `perf_event_attr` describes this record, /// but only if all potential attrs include `PERF_SAMPLE_IDENTIFIER`. /// Once the record's ID is known, this ID can be mapped to the right attr, /// and then the information from the attr can be used to parse the rest of this record. pub fn get_record_identifier( record_type: RecordType, mut data: RawData, sample_id_all: bool, ) -> Option { if record_type.is_user_type() { None } else if record_type == RecordType::SAMPLE { // if IDENTIFIER is set, every SAMPLE record starts with the event ID. data.read_u64::().ok() } else if sample_id_all { // if IDENTIFIER and SAMPLE_ID_ALL are set, every non-SAMPLE record ends with the event ID. let id_offset_from_start = data.len().checked_sub(8)?; data.skip(id_offset_from_start).ok()?; data.read_u64::().ok() } else { None } } /// Get the ID from an event record, with the help of `RecordIdParseInfo`. /// /// This can be used if it is not known which `perf_event_attr` describes this record, /// but only if all potential attrs have the same `RecordIdParseInfo`. /// Once the record's ID is known, this ID can be mapped to the right attr, /// and then the information from the attr can be used to parse the rest of this record. pub fn get_record_id( record_type: RecordType, mut data: RawData, parse_info: &RecordIdParseInfo, ) -> Option { if record_type.is_user_type() { return None; } if record_type == RecordType::SAMPLE { if let Some(id_offset_from_start) = parse_info.sample_record_id_offset_from_start { data.skip(id_offset_from_start as usize).ok()?; data.read_u64::().ok() } else { None } } else if let Some(id_offset_from_end) = parse_info.nonsample_record_id_offset_from_end { let id_offset_from_start = data.len().checked_sub(id_offset_from_end as usize)?; data.skip(id_offset_from_start).ok()?; data.read_u64::().ok() } else { None } } /// Get the timestamp from an event record, with the help of `RecordParseInfo`. /// /// This can be used for record sorting, without having to wrap the record into /// a `RawRecord`.o pub fn get_record_timestamp( record_type: RecordType, mut data: RawData, parse_info: &RecordParseInfo, ) -> Option { if record_type.is_user_type() { return None; } if record_type == RecordType::SAMPLE { if let Some(time_offset_from_start) = parse_info.sample_record_time_offset_from_start { data.skip(time_offset_from_start as usize).ok()?; data.read_u64::().ok() } else { None } } else if let Some(time_offset_from_end) = parse_info.nonsample_record_time_offset_from_end { let time_offset_from_start = data.len().checked_sub(time_offset_from_end as usize)?; data.skip(time_offset_from_start).ok()?; data.read_u64::().ok() } else { None } } /// A fully parsed event record. #[derive(Debug, Clone, PartialEq, Eq)] #[allow(clippy::large_enum_variant)] #[non_exhaustive] pub enum EventRecord<'a> { Sample(SampleRecord<'a>), Comm(CommOrExecRecord<'a>), Exit(ForkOrExitRecord), Fork(ForkOrExitRecord), Mmap(MmapRecord<'a>), Mmap2(Mmap2Record<'a>), Lost(LostRecord), Throttle(ThrottleRecord), Unthrottle(ThrottleRecord), ContextSwitch(ContextSwitchRecord), Raw(RawEventRecord<'a>), } #[derive(Debug, Clone, PartialEq, Eq)] pub struct ForkOrExitRecord { pub pid: i32, pub ppid: i32, pub tid: i32, pub ptid: i32, pub timestamp: u64, } impl ForkOrExitRecord { pub fn parse(data: RawData) -> Result { let mut cur = data; let pid = cur.read_i32::()?; let ppid = cur.read_i32::()?; let tid = cur.read_i32::()?; let ptid = cur.read_i32::()?; let timestamp = cur.read_u64::()?; Ok(Self { pid, ppid, tid, ptid, timestamp, }) } } #[derive(Clone, PartialEq, Eq)] pub struct CommOrExecRecord<'a> { pub pid: i32, pub tid: i32, pub name: RawData<'a>, pub is_execve: bool, } impl<'a> CommOrExecRecord<'a> { pub fn parse(data: RawData<'a>, misc: u16) -> Result { let mut cur = data; let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; let name = cur.read_string().unwrap_or(cur); // TODO: return error if no string terminator found // TODO: Maybe feature-gate this on 3.16+ let is_execve = misc & constants::PERF_RECORD_MISC_COMM_EXEC != 0; Ok(Self { pid, tid, name, is_execve, }) } } impl<'a> fmt::Debug for CommOrExecRecord<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { use std::str; let mut map = fmt.debug_map(); map.entry(&"pid", &self.pid).entry(&"tid", &self.tid); if let Ok(string) = str::from_utf8(&self.name.as_slice()) { map.entry(&"name", &string); } else { map.entry(&"name", &self.name); } map.entry(&"is_execve", &self.is_execve); map.finish() } } /// These aren't emitted by the kernel any more - the kernel uses MMAP2 events /// these days. /// However, `perf record` still emits synthetic MMAP events (not MMAP2!) for /// the kernel image. So if you want to symbolicate kernel addresses you still /// need to process these. /// The kernel image MMAP events have pid -1. #[derive(Clone, PartialEq, Eq)] pub struct MmapRecord<'a> { pub pid: i32, pub tid: i32, pub address: u64, pub length: u64, pub page_offset: u64, pub is_executable: bool, pub cpu_mode: CpuMode, pub path: RawData<'a>, } impl<'a> MmapRecord<'a> { pub fn parse(data: RawData<'a>, misc: u16) -> Result { let mut cur = data; // struct { // struct perf_event_header header; // // u32 pid, tid; // u64 addr; // u64 len; // u64 pgoff; // char filename[]; // struct sample_id sample_id; // }; let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; let address = cur.read_u64::()?; let length = cur.read_u64::()?; let page_offset = cur.read_u64::()?; let path = cur.read_string().unwrap_or(cur); // TODO: return error if no string terminator found let is_executable = misc & constants::PERF_RECORD_MISC_MMAP_DATA == 0; Ok(MmapRecord { pid, tid, address, length, page_offset, is_executable, cpu_mode: CpuMode::from_misc(misc), path, }) } } impl<'a> fmt::Debug for MmapRecord<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.debug_map() .entry(&"pid", &self.pid) .entry(&"tid", &self.tid) .entry(&"address", &HexValue(self.address)) .entry(&"length", &HexValue(self.length)) .entry(&"page_offset", &HexValue(self.page_offset)) .entry(&"cpu_mode", &self.cpu_mode) .entry(&"path", &&*String::from_utf8_lossy(&self.path.as_slice())) .finish() } } #[derive(Debug, Clone, PartialEq, Eq)] pub enum Mmap2FileId { InodeAndVersion(Mmap2InodeAndVersion), BuildId(Vec), } #[derive(Clone, PartialEq, Eq)] pub struct Mmap2Record<'a> { pub pid: i32, pub tid: i32, pub address: u64, pub length: u64, pub page_offset: u64, pub file_id: Mmap2FileId, pub protection: u32, pub flags: u32, pub cpu_mode: CpuMode, pub path: RawData<'a>, } impl<'a> Mmap2Record<'a> { pub fn parse(data: RawData<'a>, misc: u16) -> Result { let mut cur = data; let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; let address = cur.read_u64::()?; let length = cur.read_u64::()?; let page_offset = cur.read_u64::()?; let file_id = if misc & constants::PERF_RECORD_MISC_MMAP_BUILD_ID != 0 { let build_id_len = cur.read_u8()?; assert!(build_id_len <= 20); let _align = cur.read_u8()?; let _align = cur.read_u16::()?; let mut build_id_bytes = [0; 20]; cur.read_exact(&mut build_id_bytes)?; Mmap2FileId::BuildId(build_id_bytes[..build_id_len as usize].to_owned()) } else { let major = cur.read_u32::()?; let minor = cur.read_u32::()?; let inode = cur.read_u64::()?; let inode_generation = cur.read_u64::()?; Mmap2FileId::InodeAndVersion(Mmap2InodeAndVersion { major, minor, inode, inode_generation, }) }; let protection = cur.read_u32::()?; let flags = cur.read_u32::()?; let path = cur.read_string().unwrap_or(cur); // TODO: return error if no string terminator found Ok(Mmap2Record { pid, tid, address, length, page_offset, file_id, protection, flags, cpu_mode: CpuMode::from_misc(misc), path, }) } } impl<'a> fmt::Debug for Mmap2Record<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.debug_map() .entry(&"pid", &self.pid) .entry(&"tid", &self.tid) .entry(&"address", &HexValue(self.address)) .entry(&"length", &HexValue(self.length)) .entry(&"page_offset", &HexValue(self.page_offset)) // .entry(&"major", &self.major) // .entry(&"minor", &self.minor) // .entry(&"inode", &self.inode) // .entry(&"inode_generation", &self.inode_generation) .entry(&"protection", &HexValue(self.protection as _)) .entry(&"flags", &HexValue(self.flags as _)) .entry(&"cpu_mode", &self.cpu_mode) .entry(&"path", &&*String::from_utf8_lossy(&self.path.as_slice())) .finish() } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Mmap2InodeAndVersion { pub major: u32, pub minor: u32, pub inode: u64, pub inode_generation: u64, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct LostRecord { pub id: u64, pub count: u64, } impl LostRecord { pub fn parse(data: RawData) -> Result { let mut cur = data; let id = cur.read_u64::()?; let count = cur.read_u64::()?; Ok(LostRecord { id, count }) } } #[derive(Debug, Clone, PartialEq, Eq)] pub struct ThrottleRecord { pub id: u64, pub timestamp: u64, } impl ThrottleRecord { pub fn parse(data: RawData) -> Result { let mut cur = data; let timestamp = cur.read_u64::()?; let id = cur.read_u64::()?; Ok(ThrottleRecord { id, timestamp }) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum ContextSwitchRecord { In { prev_pid: Option, prev_tid: Option, }, Out { next_pid: Option, next_tid: Option, preempted: TaskWasPreempted, }, } impl ContextSwitchRecord { pub fn from_misc(misc: u16) -> Self { Self::from_misc_pid_tid(misc, None, None) } pub fn parse_cpu_wide(data: RawData, misc: u16) -> Result { let mut cur = data; let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; Ok(Self::from_misc_pid_tid(misc, Some(pid), Some(tid))) } pub fn from_misc_pid_tid(misc: u16, pid: Option, tid: Option) -> Self { let is_out = misc & constants::PERF_RECORD_MISC_SWITCH_OUT != 0; if is_out { let is_out_preempt = misc & constants::PERF_RECORD_MISC_SWITCH_OUT_PREEMPT != 0; ContextSwitchRecord::Out { next_pid: pid, next_tid: tid, preempted: if is_out_preempt { TaskWasPreempted::Yes } else { TaskWasPreempted::No }, } } else { ContextSwitchRecord::In { prev_pid: pid, prev_tid: tid, } } } } /// Whether a task was in the `TASK_RUNNING` state when it was switched /// away from. /// /// This helps understanding whether a workload is CPU or IO bound. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum TaskWasPreempted { /// When switching out, the task was in the `TASK_RUNNING` state. Yes, /// When switching out, the task was in a non-running state. No, } /// An unparsed event record. /// /// This can be converted into a parsed record by calling `.parse()`. /// /// The raw record also provides access to "common data" like the ID, timestamp, /// tid etc., i.e. the information that was requested with [`SampleFormat`](crate::SampleFormat) and /// [`AttrFlags::SAMPLE_ID_ALL`](crate::AttrFlags::SAMPLE_ID_ALL). #[derive(Clone, PartialEq, Eq)] pub struct RawEventRecord<'a> { /// The record type. Must be a builtin type, i.e. not a user type. pub record_type: RecordType, /// The `misc` value on this record. pub misc: u16, /// The raw bytes in the body of this record. pub data: RawData<'a>, /// The parse info from our corresponding evnt's attr. pub parse_info: RecordParseInfo, } impl<'a> RawEventRecord<'a> { /// Create a new `RawEventRecord`. Must only be called if `record_type.is_builtin_type()` is `true`. pub fn new( record_type: RecordType, misc: u16, data: RawData<'a>, parse_info: RecordParseInfo, ) -> Self { Self { record_type, misc, data, parse_info, } } /// Parse "common data" on this record, see [`CommonData`]. /// /// The available information is determined by the event attr, specifically /// by the requested [`SampleFormat`](crate::SampleFormat) and by the /// presence of the [`AttrFlags::SAMPLE_ID_ALL`](crate::AttrFlags::SAMPLE_ID_ALL) /// flag: The `SampleFormat` determines the available fields, and the /// `SAMPLE_ID_ALL` flag determines the record types on which these fields /// are available. If `SAMPLE_ID_ALL` is set, the requested fields are /// available on all records, otherwise only on sample records /// ([`RecordType::SAMPLE`]). pub fn common_data(&self) -> Result { if self.record_type.is_user_type() { return Ok(Default::default()); } if self.record_type == RecordType::SAMPLE { CommonData::parse_sample(self.data, &self.parse_info) } else { CommonData::parse_nonsample(self.data, &self.parse_info) } } /// The record timestamp, if available. pub fn timestamp(&self) -> Option { match self.parse_info.endian { Endianness::LittleEndian => self.timestamp_impl::(), Endianness::BigEndian => self.timestamp_impl::(), } } fn timestamp_impl(&self) -> Option { get_record_timestamp::(self.record_type, self.data, &self.parse_info) } /// The ID, if available. pub fn id(&self) -> Option { match self.parse_info.endian { Endianness::LittleEndian => self.id_impl::(), Endianness::BigEndian => self.id_impl::(), } } fn id_impl(&self) -> Option { get_record_id::(self.record_type, self.data, &self.parse_info.id_parse_info) } /// Parses this raw record into an [`EventRecord`]. pub fn parse(&self) -> Result, std::io::Error> { match self.parse_info.endian { Endianness::LittleEndian => self.parse_impl::(), Endianness::BigEndian => self.parse_impl::(), } } fn parse_impl(&self) -> Result, std::io::Error> { let parse_info = &self.parse_info; let event = match self.record_type { // Kernel built-in record types RecordType::MMAP => EventRecord::Mmap(MmapRecord::parse::(self.data, self.misc)?), RecordType::LOST => EventRecord::Lost(LostRecord::parse::(self.data)?), RecordType::COMM => { EventRecord::Comm(CommOrExecRecord::parse::(self.data, self.misc)?) } RecordType::EXIT => EventRecord::Exit(ForkOrExitRecord::parse::(self.data)?), RecordType::THROTTLE => EventRecord::Throttle(ThrottleRecord::parse::(self.data)?), RecordType::UNTHROTTLE => { EventRecord::Unthrottle(ThrottleRecord::parse::(self.data)?) } RecordType::FORK => EventRecord::Fork(ForkOrExitRecord::parse::(self.data)?), // READ RecordType::SAMPLE => { EventRecord::Sample(SampleRecord::parse::(self.data, self.misc, parse_info)?) } RecordType::MMAP2 => EventRecord::Mmap2(Mmap2Record::parse::(self.data, self.misc)?), // AUX // ITRACE_START // LOST_SAMPLES RecordType::SWITCH => { EventRecord::ContextSwitch(ContextSwitchRecord::from_misc(self.misc)) } RecordType::SWITCH_CPU_WIDE => EventRecord::ContextSwitch( ContextSwitchRecord::parse_cpu_wide::(self.data, self.misc)?, ), // NAMESPACES // KSYMBOL // BPF_EVENT // CGROUP // TEXT_POKE // AUX_OUTPUT_HW_ID _ => EventRecord::Raw(self.clone()), }; Ok(event) } } impl<'a> fmt::Debug for RawEventRecord<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.debug_map() .entry(&"record_type", &self.record_type) .entry(&"misc", &self.misc) .entry(&"data.len", &self.data.len()) .finish() } } linux-perf-event-reader-0.10.0/src/lib.rs000064400000000000000000000070131046102023000162560ustar 00000000000000//! # linux-perf-event-reader //! //! This crate lets you parse Linux perf events and associated structures. //! //! ## Example //! //! ```rust //! use linux_perf_event_reader::{ //! CommOrExecRecord, Endianness, EventRecord, PerfEventAttr, RawData, RawEventRecord, //! RecordParseInfo, RecordType //! }; //! //! # fn it_works() { //! // Read the perf_event_attr data. //! let attr_data = [ //! 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 3, 0, 0, 0, 0, 0, 0, 47, 177, 0, //! 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 183, 215, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 15, //! 255, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //! 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 128, 0, //! 0, 0, 0, 0, 0, 0, //! ]; //! let (attr, _size) = //! PerfEventAttr::parse::<_, byteorder::LittleEndian>(&attr_data[..]).unwrap(); //! let parse_info = RecordParseInfo::new(&attr, Endianness::LittleEndian); //! //! let body = b"lG\x08\0lG\x08\0dump_syms\0\0\0\0\0\0\0lG\x08\0lG\x08\08\x1b\xf8\x18hX\x04\0"; //! let body_raw_data = RawData::from(&body[..]); //! let raw_record = RawEventRecord::new(RecordType::COMM, 0x2000, body_raw_data, parse_info); //! let parsed_record = raw_record.parse().unwrap(); //! //! assert_eq!( //! parsed_record, //! EventRecord::Comm(CommOrExecRecord { //! pid: 542572, //! tid: 542572, //! name: RawData::Single(b"dump_syms"), //! is_execve: true //! }) //! ); //! # } //! ``` mod common_data; pub mod constants; mod endian; mod event_record; mod parse_info; mod perf_event; mod raw_data; mod registers; mod sample; mod types; mod utils; pub use common_data::*; pub use endian::*; pub use event_record::*; pub use parse_info::*; pub use perf_event::*; pub use raw_data::*; pub use registers::*; pub use sample::*; pub use types::*; #[cfg(test)] mod test { use crate::{ CommOrExecRecord, Endianness, EventRecord, PerfEventAttr, RawData, RawEventRecord, RecordParseInfo, RecordType, }; #[test] fn it_works() { // Read the perf_event_attr data. let attr_data = [ 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 229, 3, 0, 0, 0, 0, 0, 0, 47, 177, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 3, 183, 215, 97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 15, 255, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, 0, 0, 0, 0, 0, ]; let (attr, _size) = PerfEventAttr::parse::<_, byteorder::LittleEndian>(&attr_data[..]).unwrap(); let parse_info = RecordParseInfo::new(&attr, Endianness::LittleEndian); let body = b"lG\x08\0lG\x08\0dump_syms\0\0\0\0\0\0\0lG\x08\0lG\x08\08\x1b\xf8\x18hX\x04\0"; let body_raw_data = RawData::from(&body[..]); let raw_record = RawEventRecord::new(RecordType::COMM, 0x2000, body_raw_data, parse_info); let parsed_record = raw_record.parse().unwrap(); assert_eq!( parsed_record, EventRecord::Comm(CommOrExecRecord { pid: 542572, tid: 542572, name: RawData::Single(b"dump_syms"), is_execve: true }) ); } } linux-perf-event-reader-0.10.0/src/parse_info.rs000064400000000000000000000143431046102023000176410ustar 00000000000000use crate::{AttrFlags, BranchSampleFormat, Endianness, PerfEventAttr, ReadFormat, SampleFormat}; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct RecordParseInfo { pub endian: Endianness, pub sample_format: SampleFormat, pub branch_sample_format: BranchSampleFormat, pub read_format: ReadFormat, pub common_data_offset_from_end: Option, // 0..=48 pub sample_regs_user: u64, pub user_regs_count: u8, // 0..=64 pub sample_regs_intr: u64, pub intr_regs_count: u8, // 0..=64 pub id_parse_info: RecordIdParseInfo, pub nonsample_record_time_offset_from_end: Option, // 0..=40 pub sample_record_time_offset_from_start: Option, // 0..=32 } #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct RecordIdParseInfo { pub nonsample_record_id_offset_from_end: Option, // 0..=32 pub sample_record_id_offset_from_start: Option, // 0..=24 } impl RecordParseInfo { pub fn new(attr: &PerfEventAttr, endian: Endianness) -> Self { let sample_format = attr.sample_format; let branch_sample_format = attr.branch_sample_format; let read_format = attr.read_format; // struct sample_id { // { u32 pid, tid; } /* if PERF_SAMPLE_TID set */ // { u64 time; } /* if PERF_SAMPLE_TIME set */ // { u64 id; } /* if PERF_SAMPLE_ID set */ // { u64 stream_id;} /* if PERF_SAMPLE_STREAM_ID set */ // { u32 cpu, res; } /* if PERF_SAMPLE_CPU set */ // { u64 id; } /* if PERF_SAMPLE_IDENTIFIER set */ // }; let common_data_offset_from_end = if attr.flags.contains(AttrFlags::SAMPLE_ID_ALL) { Some( sample_format .intersection( SampleFormat::TID | SampleFormat::TIME | SampleFormat::ID | SampleFormat::STREAM_ID | SampleFormat::CPU | SampleFormat::IDENTIFIER, ) .bits() .count_ones() as u8 * 8, ) } else { None }; let sample_regs_user = attr.sample_regs_user; let user_regs_count = sample_regs_user.count_ones() as u8; let sample_regs_intr = attr.sample_regs_intr; let intr_regs_count = sample_regs_intr.count_ones() as u8; let nonsample_record_time_offset_from_end = if attr.flags.contains(AttrFlags::SAMPLE_ID_ALL) && sample_format.contains(SampleFormat::TIME) { Some( sample_format .intersection( SampleFormat::TIME | SampleFormat::ID | SampleFormat::STREAM_ID | SampleFormat::CPU | SampleFormat::IDENTIFIER, ) .bits() .count_ones() as u8 * 8, ) } else { None }; // { u64 id; } && PERF_SAMPLE_IDENTIFIER // { u64 ip; } && PERF_SAMPLE_IP // { u32 pid; u32 tid; } && PERF_SAMPLE_TID // { u64 time; } && PERF_SAMPLE_TIME // { u64 addr; } && PERF_SAMPLE_ADDR // { u64 id; } && PERF_SAMPLE_ID let sample_record_time_offset_from_start = if sample_format.contains(SampleFormat::TIME) { Some( sample_format .intersection(SampleFormat::IDENTIFIER | SampleFormat::IP | SampleFormat::TID) .bits() .count_ones() as u8 * 8, ) } else { None }; Self { endian, sample_format, branch_sample_format, read_format, common_data_offset_from_end, sample_regs_user, user_regs_count, sample_regs_intr, intr_regs_count, nonsample_record_time_offset_from_end, sample_record_time_offset_from_start, id_parse_info: RecordIdParseInfo::new(attr), } } } impl RecordIdParseInfo { pub fn new(attr: &PerfEventAttr) -> Self { let sample_format = attr.sample_format; let nonsample_record_id_offset_from_end = if attr.flags.contains(AttrFlags::SAMPLE_ID_ALL) && sample_format.intersects(SampleFormat::ID | SampleFormat::IDENTIFIER) { if sample_format.contains(SampleFormat::IDENTIFIER) { Some(8) } else { Some( sample_format .intersection( SampleFormat::ID | SampleFormat::STREAM_ID | SampleFormat::CPU | SampleFormat::IDENTIFIER, ) .bits() .count_ones() as u8 * 8, ) } } else { None }; // { u64 id; } && PERF_SAMPLE_IDENTIFIER // { u64 ip; } && PERF_SAMPLE_IP // { u32 pid; u32 tid; } && PERF_SAMPLE_TID // { u64 time; } && PERF_SAMPLE_TIME // { u64 addr; } && PERF_SAMPLE_ADDR // { u64 id; } && PERF_SAMPLE_ID let sample_record_id_offset_from_start = if sample_format.contains(SampleFormat::IDENTIFIER) { Some(0) } else if sample_format.contains(SampleFormat::ID) { Some( sample_format .intersection( SampleFormat::IP | SampleFormat::TID | SampleFormat::TIME | SampleFormat::ADDR, ) .bits() .count_ones() as u8 * 8, ) } else { None }; Self { nonsample_record_id_offset_from_end, sample_record_id_offset_from_start, } } } linux-perf-event-reader-0.10.0/src/perf_event.rs000064400000000000000000000531221046102023000176470ustar 00000000000000use crate::constants::*; use crate::types::*; use byteorder::{ByteOrder, ReadBytesExt}; use std::io; use std::io::Read; use std::num::NonZeroU64; /// `perf_event_header` #[derive(Debug, Clone, Copy)] pub struct PerfEventHeader { pub type_: u32, pub misc: u16, pub size: u16, } impl PerfEventHeader { pub const STRUCT_SIZE: usize = 4 + 2 + 2; pub fn parse(mut reader: R) -> Result { let type_ = reader.read_u32::()?; let misc = reader.read_u16::()?; let size = reader.read_u16::()?; Ok(Self { type_, misc, size }) } } /// `perf_event_attr` #[derive(Debug, Clone, Copy)] pub struct PerfEventAttr { /// The type of the perf event. pub type_: PerfEventType, /// The sampling policy. pub sampling_policy: SamplingPolicy, /// Specifies values included in sample. (original name `sample_type`) pub sample_format: SampleFormat, /// Specifies the structure values returned by read() on a perf event fd, /// see [`ReadFormat`]. pub read_format: ReadFormat, /// Bitset of flags. pub flags: AttrFlags, /// The wake-up policy. pub wakeup_policy: WakeupPolicy, /// Branch-sample specific flags. pub branch_sample_format: BranchSampleFormat, /// Defines set of user regs to dump on samples. /// See asm/perf_regs.h for details. pub sample_regs_user: u64, /// Defines size of the user stack to dump on samples. pub sample_stack_user: u32, /// The clock ID. pub clock: PerfClock, /// 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. pub sample_regs_intr: u64, /// Wakeup watermark for AUX area pub aux_watermark: u32, /// When collecting stacks, this is the maximum number of stack frames /// (user + kernel) to collect. pub sample_max_stack: u16, /// When sampling AUX events, this is the size of the AUX sample. pub aux_sample_size: u32, /// User provided data if sigtrap=1, passed back to user via /// siginfo_t::si_perf_data, e.g. to permit user to identify the event. /// Note, siginfo_t::si_perf_data is long-sized, and sig_data will be /// truncated accordingly on 32 bit architectures. pub sig_data: u64, } impl PerfEventAttr { /// Parse from a reader. On success, this returns the parsed attribute and /// the number of bytes that were read from the reader. This matches the self-reported /// size in the attribute. pub fn parse(mut reader: R) -> Result<(Self, u64), std::io::Error> { let type_ = reader.read_u32::()?; let size = reader.read_u32::()?; let config = reader.read_u64::()?; if size < PERF_ATTR_SIZE_VER0 { return Err(io::ErrorKind::InvalidInput.into()); } let sampling_period_or_frequency = reader.read_u64::()?; let sample_type = reader.read_u64::()?; let read_format = reader.read_u64::()?; let flags = reader.read_u64::()?; let wakeup_events_or_watermark = reader.read_u32::()?; let bp_type = reader.read_u32::()?; let bp_addr_or_kprobe_func_or_uprobe_func_or_config1 = reader.read_u64::()?; let bp_len_or_kprobe_addr_or_probe_offset_or_config2 = if size >= PERF_ATTR_SIZE_VER1 { reader.read_u64::()? } else { 0 }; let branch_sample_type = if size >= PERF_ATTR_SIZE_VER2 { reader.read_u64::()? } else { 0 }; let (sample_regs_user, sample_stack_user, clockid) = if size >= PERF_ATTR_SIZE_VER3 { let sample_regs_user = reader.read_u64::()?; let sample_stack_user = reader.read_u32::()?; let clockid = reader.read_u32::()?; (sample_regs_user, sample_stack_user, clockid) } else { (0, 0, 0) }; let sample_regs_intr = if size >= PERF_ATTR_SIZE_VER4 { reader.read_u64::()? } else { 0 }; let (aux_watermark, sample_max_stack) = if size >= PERF_ATTR_SIZE_VER5 { let aux_watermark = reader.read_u32::()?; let sample_max_stack = reader.read_u16::()?; let __reserved_2 = reader.read_u16::()?; (aux_watermark, sample_max_stack) } else { (0, 0) }; let aux_sample_size = if size >= PERF_ATTR_SIZE_VER6 { let aux_sample_size = reader.read_u32::()?; let __reserved_3 = reader.read_u32::()?; aux_sample_size } else { 0 }; let sig_data = if size >= PERF_ATTR_SIZE_VER7 { reader.read_u64::()? } else { 0 }; // Consume any remaining bytes. if size > PERF_ATTR_SIZE_VER7 { let remaining = size - PERF_ATTR_SIZE_VER7; io::copy(&mut reader.by_ref().take(remaining.into()), &mut io::sink())?; } let flags = AttrFlags::from_bits_truncate(flags); let type_ = PerfEventType::parse( type_, bp_type, config, bp_addr_or_kprobe_func_or_uprobe_func_or_config1, bp_len_or_kprobe_addr_or_probe_offset_or_config2, ) .ok_or(io::ErrorKind::InvalidInput)?; // If AttrFlags::FREQ is set in `flags`, this is the sample frequency, // otherwise it is the sample period. // // ```c // union { // /// Period of sampling // __u64 sample_period; // /// Frequency of sampling // __u64 sample_freq; // }; // ``` let sampling_policy = if flags.contains(AttrFlags::FREQ) { SamplingPolicy::Frequency(sampling_period_or_frequency) } else if let Some(period) = NonZeroU64::new(sampling_period_or_frequency) { SamplingPolicy::Period(period) } else { SamplingPolicy::NoSampling }; let wakeup_policy = if flags.contains(AttrFlags::WATERMARK) { WakeupPolicy::Watermark(wakeup_events_or_watermark) } else { WakeupPolicy::EventCount(wakeup_events_or_watermark) }; let clock = if flags.contains(AttrFlags::USE_CLOCKID) { let clockid = ClockId::from_u32(clockid).ok_or(io::ErrorKind::InvalidInput)?; PerfClock::ClockId(clockid) } else { PerfClock::Default }; let attr = Self { type_, sampling_policy, sample_format: SampleFormat::from_bits_truncate(sample_type), read_format: ReadFormat::from_bits_truncate(read_format), flags, wakeup_policy, branch_sample_format: BranchSampleFormat::from_bits_truncate(branch_sample_type), sample_regs_user, sample_stack_user, clock, sample_regs_intr, aux_watermark, sample_max_stack, aux_sample_size, sig_data, }; Ok((attr, size.into())) } } /// The type of perf event #[derive(Debug, Clone, Copy)] pub enum PerfEventType { /// A hardware perf event. (`PERF_TYPE_HARDWARE`) Hardware(HardwareEventId, PmuTypeId), /// A software perf event. (`PERF_TYPE_SOFTWARE`) /// /// 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). Software(SoftwareCounterType), /// A tracepoint perf event. (`PERF_TYPE_TRACEPOINT`) Tracepoint(u64), /// A hardware cache perf event. (`PERF_TYPE_HW_CACHE`) /// /// Selects a certain combination of CacheId, CacheOp, CacheOpResult, PMU type ID. /// /// ```plain /// { L1-D, L1-I, LLC, ITLB, DTLB, BPU, NODE } x /// { read, write, prefetch } x /// { accesses, misses } /// ``` HwCache( HardwareCacheId, HardwareCacheOp, HardwareCacheOpResult, PmuTypeId, ), /// A hardware breakpoint perf event. (`PERF_TYPE_BREAKPOINT`) /// /// Breakpoints can be read/write accesses to an address as well as /// execution of an instruction address. Breakpoint(HwBreakpointType, HwBreakpointAddr, HwBreakpointLen), /// Dynamic PMU /// /// `(pmu, config, config1, config2)` /// /// Acceptable values for each of `config`, `config1` and `config2` /// parameters are defined by corresponding entries in /// `/sys/bus/event_source/devices//format/*`. /// /// From the `perf_event_open` man page: /// > Since Linux 2.6.38, perf_event_open() can support multiple PMUs. To /// > enable this, a value exported by the kernel can be used in the type /// > field to indicate which PMU to use. The value to use can be found in /// > the sysfs filesystem: there is a subdirectory per PMU instance under /// > /sys/bus/event_source/devices. In each subdirectory there is a type /// > file whose content is an integer that can be used in the type field. /// > For instance, /sys/bus/event_source/devices/cpu/type contains the /// > value for the core CPU PMU, which is usually 4. /// /// (I don't fully understand this - the value 4 also means `PERF_TYPE_RAW`. /// Maybe the type `Raw` is just one of those dynamic PMUs, usually "core"?) /// /// Among the "dynamic PMU" values, there are two special values for /// kprobes and uprobes: /// /// > kprobe and uprobe (since Linux 4.17) /// > These two dynamic PMUs create a kprobe/uprobe and attach it to the /// > file descriptor generated by perf_event_open. The kprobe/uprobe will /// > be destroyed on the destruction of the file descriptor. See fields /// > kprobe_func, uprobe_path, kprobe_addr, and probe_offset for more details. /// /// ```c /// union { /// __u64 kprobe_func; /* for perf_kprobe */ /// __u64 uprobe_path; /* for perf_uprobe */ /// __u64 config1; /* extension of config */ /// }; /// /// union { /// __u64 kprobe_addr; /* when kprobe_func == NULL */ /// __u64 probe_offset; /* for perf_[k,u]probe */ /// __u64 config2; /* extension of config1 */ /// }; /// ``` DynamicPmu(u32, u64, u64, u64), } /// PMU type ID /// /// The PMU type ID allows selecting whether to observe only "atom", only "core", /// or both. If the PMU type ID is zero, both "atom" and "core" are observed. /// To observe just one of them, the PMU type ID needs to be set to the value of /// `/sys/devices/cpu_atom/type` or of `/sys/devices/cpu_core/type`. #[derive(Debug, Clone, Copy)] pub struct PmuTypeId(pub u32); /// The address of the breakpoint. /// /// For execution breakpoints, this is the memory address of the instruction /// of interest; for read and write breakpoints, it is the memory address of /// the memory location of interest. #[derive(Debug, Clone, Copy)] pub struct HwBreakpointAddr(pub u64); /// The length of the breakpoint being measured. /// /// Options are `HW_BREAKPOINT_LEN_1`, `HW_BREAKPOINT_LEN_2`, /// `HW_BREAKPOINT_LEN_4`, and `HW_BREAKPOINT_LEN_8`. For an /// execution breakpoint, set this to sizeof(long). #[derive(Debug, Clone, Copy)] pub struct HwBreakpointLen(pub u64); impl PerfEventType { pub fn parse( type_: u32, bp_type: u32, config: u64, config1: u64, config2: u64, ) -> Option { let t = match type_ { PERF_TYPE_HARDWARE => { // Config format: 0xEEEEEEEE000000AA // // - AA: hardware event ID // - EEEEEEEE: PMU type ID let hardware_event_id = (config & 0xff) as u8; let pmu_type = PmuTypeId((config >> 32) as u32); Self::Hardware(HardwareEventId::parse(hardware_event_id)?, pmu_type) } PERF_TYPE_SOFTWARE => Self::Software(SoftwareCounterType::parse(config)?), PERF_TYPE_TRACEPOINT => Self::Tracepoint(config), PERF_TYPE_HW_CACHE => { // Config format: 0xEEEEEEEE00DDCCBB // // - BB: hardware cache ID // - CC: hardware cache op ID // - DD: hardware cache op result ID // - EEEEEEEE: PMU type ID let cache_id = config as u8; let cache_op_id = (config >> 8) as u8; let cache_op_result = (config >> 16) as u8; let pmu_type = PmuTypeId((config >> 32) as u32); Self::HwCache( HardwareCacheId::parse(cache_id)?, HardwareCacheOp::parse(cache_op_id)?, HardwareCacheOpResult::parse(cache_op_result)?, pmu_type, ) } PERF_TYPE_BREAKPOINT => { let bp_type = HwBreakpointType::from_bits_truncate(bp_type); Self::Breakpoint(bp_type, HwBreakpointAddr(config1), HwBreakpointLen(config2)) } _ => Self::DynamicPmu(type_, config, config1, config2), // PERF_TYPE_RAW is handled as part of DynamicPmu. }; Some(t) } } #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum HardwareEventId { /// `PERF_COUNT_HW_CPU_CYCLES` CpuCycles, /// `PERF_COUNT_HW_INSTRUCTIONS` Instructions, /// `PERF_COUNT_HW_CACHE_REFERENCES` CacheReferences, /// `PERF_COUNT_HW_CACHE_MISSES` CacheMisses, /// `PERF_COUNT_HW_BRANCH_INSTRUCTIONS` BranchInstructions, /// `PERF_COUNT_HW_BRANCH_MISSES` BranchMisses, /// `PERF_COUNT_HW_BUS_CYCLES` BusCycles, /// `PERF_COUNT_HW_STALLED_CYCLES_FRONTEND` StalledCyclesFrontend, /// `PERF_COUNT_HW_STALLED_CYCLES_BACKEND` StalledCyclesBackend, /// `PERF_COUNT_HW_REF_CPU_CYCLES` RefCpuCycles, } impl HardwareEventId { pub fn parse(hardware_event_id: u8) -> Option { let t = match hardware_event_id { PERF_COUNT_HW_CPU_CYCLES => Self::CpuCycles, PERF_COUNT_HW_INSTRUCTIONS => Self::Instructions, PERF_COUNT_HW_CACHE_REFERENCES => Self::CacheReferences, PERF_COUNT_HW_CACHE_MISSES => Self::CacheMisses, PERF_COUNT_HW_BRANCH_INSTRUCTIONS => Self::BranchInstructions, PERF_COUNT_HW_BRANCH_MISSES => Self::BranchMisses, PERF_COUNT_HW_BUS_CYCLES => Self::BusCycles, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND => Self::StalledCyclesFrontend, PERF_COUNT_HW_STALLED_CYCLES_BACKEND => Self::StalledCyclesBackend, PERF_COUNT_HW_REF_CPU_CYCLES => Self::RefCpuCycles, _ => return None, }; Some(t) } } #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum SoftwareCounterType { /// `PERF_COUNT_SW_CPU_CLOCK` CpuClock, /// `PERF_COUNT_SW_TASK_CLOCK` TaskClock, /// `PERF_COUNT_SW_PAGE_FAULTS` PageFaults, /// `PERF_COUNT_SW_CONTEXT_SWITCHES` ContextSwitches, /// `PERF_COUNT_SW_CPU_MIGRATIONS` CpuMigrations, /// `PERF_COUNT_SW_PAGE_FAULTS_MIN` PageFaultsMin, /// `PERF_COUNT_SW_PAGE_FAULTS_MAJ` PageFaultsMaj, /// `PERF_COUNT_SW_ALIGNMENT_FAULTS` AlignmentFaults, /// `PERF_COUNT_SW_EMULATION_FAULTS` EmulationFaults, /// `PERF_COUNT_SW_DUMMY` Dummy, /// `PERF_COUNT_SW_BPF_OUTPUT` BpfOutput, /// `PERF_COUNT_SW_CGROUP_SWITCHES` CgroupSwitches, } impl SoftwareCounterType { pub fn parse(config: u64) -> Option { let t = match config { PERF_COUNT_SW_CPU_CLOCK => Self::CpuClock, PERF_COUNT_SW_TASK_CLOCK => Self::TaskClock, PERF_COUNT_SW_PAGE_FAULTS => Self::PageFaults, PERF_COUNT_SW_CONTEXT_SWITCHES => Self::ContextSwitches, PERF_COUNT_SW_CPU_MIGRATIONS => Self::CpuMigrations, PERF_COUNT_SW_PAGE_FAULTS_MIN => Self::PageFaultsMin, PERF_COUNT_SW_PAGE_FAULTS_MAJ => Self::PageFaultsMaj, PERF_COUNT_SW_ALIGNMENT_FAULTS => Self::AlignmentFaults, PERF_COUNT_SW_EMULATION_FAULTS => Self::EmulationFaults, PERF_COUNT_SW_DUMMY => Self::Dummy, PERF_COUNT_SW_BPF_OUTPUT => Self::BpfOutput, PERF_COUNT_SW_CGROUP_SWITCHES => Self::CgroupSwitches, _ => return None, }; Some(t) } } #[derive(Debug, Clone, Copy)] #[non_exhaustive] pub enum HardwareCacheId { /// `PERF_COUNT_HW_CACHE_L1D` L1d, /// `PERF_COUNT_HW_CACHE_L1I` L1i, /// `PERF_COUNT_HW_CACHE_LL` Ll, /// `PERF_COUNT_HW_CACHE_DTLB` Dtlb, /// `PERF_COUNT_HW_CACHE_ITLB` Itlb, /// `PERF_COUNT_HW_CACHE_BPU` Bpu, /// `PERF_COUNT_HW_CACHE_NODE` Node, } impl HardwareCacheId { pub fn parse(cache_id: u8) -> Option { let rv = match cache_id { PERF_COUNT_HW_CACHE_L1D => Self::L1d, PERF_COUNT_HW_CACHE_L1I => Self::L1i, PERF_COUNT_HW_CACHE_LL => Self::Ll, PERF_COUNT_HW_CACHE_DTLB => Self::Dtlb, PERF_COUNT_HW_CACHE_ITLB => Self::Itlb, PERF_COUNT_HW_CACHE_BPU => Self::Bpu, PERF_COUNT_HW_CACHE_NODE => Self::Node, _ => return None, }; Some(rv) } } #[derive(Debug, Clone, Copy)] pub enum HardwareCacheOp { /// `PERF_COUNT_HW_CACHE_OP_READ` Read, /// `PERF_COUNT_HW_CACHE_OP_WRITE` Write, /// `PERF_COUNT_HW_CACHE_OP_PREFETCH` Prefetch, } impl HardwareCacheOp { pub fn parse(cache_op: u8) -> Option { match cache_op { PERF_COUNT_HW_CACHE_OP_READ => Some(Self::Read), PERF_COUNT_HW_CACHE_OP_WRITE => Some(Self::Write), PERF_COUNT_HW_CACHE_OP_PREFETCH => Some(Self::Prefetch), _ => None, } } } #[derive(Debug, Clone, Copy)] pub enum HardwareCacheOpResult { /// `PERF_COUNT_HW_CACHE_RESULT_ACCESS` Access, /// `PERF_COUNT_HW_CACHE_RESULT_MISS` Miss, } impl HardwareCacheOpResult { pub fn parse(cache_op_result: u8) -> Option { match cache_op_result { PERF_COUNT_HW_CACHE_RESULT_ACCESS => Some(Self::Access), PERF_COUNT_HW_CACHE_RESULT_MISS => Some(Self::Miss), _ => None, } } } /// Sampling Policy /// /// > Events can be set to notify when a threshold is crossed, /// > indicating an overflow. [...] /// > /// > Overflows are generated only by sampling events (sample_period /// > must have a nonzero value). #[derive(Debug, Clone, Copy)] pub enum SamplingPolicy { /// `NoSampling` means that the event is a count and not a sampling event. NoSampling, /// Sets a fixed sampling period for a sampling event, in the unit of the /// observed count / event. /// /// A "sampling" event is one that generates an overflow notification every /// N events, where N is given by the sampling period. A sampling event has /// a sampling period greater than zero. /// /// When an overflow occurs, requested data is recorded in the mmap buffer. /// The `SampleFormat` bitfield controls what data is recorded on each overflow. Period(NonZeroU64), /// Sets a frequency for a sampling event, in "samples per (wall-clock) second". /// /// This uses a dynamic period which is adjusted by the kernel to hit the /// desired frequency. The rate of adjustment is a timer tick. /// /// If `SampleFormat::PERIOD` is requested, the current period at the time of /// the sample is stored in the sample. Frequency(u64), } /// Wakeup policy for "overflow notifications". This controls the point at /// which the `read` call completes. (TODO: double check this) /// /// > There are two ways to generate overflow notifications. /// > /// > The first is to set a `WakeupPolicy` /// > that will trigger if a certain number of samples or bytes have /// > been written to the mmap ring buffer. /// > /// > The other way is by use of the PERF_EVENT_IOC_REFRESH ioctl. /// > This ioctl adds to a counter that decrements each time the event /// > overflows. When nonzero, POLLIN is indicated, but once the /// > counter reaches 0 POLLHUP is indicated and the underlying event /// > is disabled. #[derive(Debug, Clone, Copy)] pub enum WakeupPolicy { /// Wake up every time N records of type `RecordType::SAMPLE` have been /// written to the mmap ring buffer. EventCount(u32), /// Wake up after N bytes of any record type have been written to the mmap /// ring buffer. /// /// To receive a wakeup after every single record, choose `Watermark(1)`. /// `Watermark(0)` is treated the same as `Watermark(1)`. Watermark(u32), } /// This allows selecting which internal Linux clock to use when generating /// timestamps. /// /// Setting a specific ClockId can make it easier to correlate perf sample /// times with timestamps generated by other tools. For example, when sampling /// applications which emit JITDUMP information, you'll usually select the /// moonotonic clock. This makes it possible to correctly order perf event /// records and JITDUMP records - those also usually use the monotonic clock. #[derive(Debug, Clone, Copy)] pub enum PerfClock { /// The default clock. If this is used, the timestamps in event records /// are obtained with `local_clock()` which is a hardware timestamp if /// available and the jiffies value if not. /// /// In practice, on x86_64 this seems to use ktime_get_ns() which is the /// number of nanoseconds since boot. Default, /// A specific clock. ClockId(ClockId), } linux-perf-event-reader-0.10.0/src/raw_data.rs000064400000000000000000000254531046102023000173020ustar 00000000000000use crate::utils::HexValue; use byteorder::{ByteOrder, NativeEndian}; use std::borrow::Cow; use std::cmp::min; use std::ops::Range; use std::{fmt, mem}; /// A slice of u8 data that can have non-contiguous backing storage split /// into two pieces, and abstracts that split away so that users can pretend /// to deal with a contiguous slice. /// /// When reading perf events from the mmap'd fd that contains the perf event /// stream, it often happens that a single record straddles the boundary between /// two mmap chunks, or is wrapped from the end to the start of a chunk. #[derive(Clone, Copy, PartialEq, Eq)] pub enum RawData<'a> { Single(&'a [u8]), Split(&'a [u8], &'a [u8]), } impl<'a> From<&'a Cow<'a, [u8]>> for RawData<'a> { fn from(data: &'a Cow<'a, [u8]>) -> Self { match *data { Cow::Owned(ref bytes) => RawData::Single(bytes.as_slice()), Cow::Borrowed(bytes) => RawData::Single(bytes), } } } impl<'a> From<&'a [u8]> for RawData<'a> { fn from(bytes: &'a [u8]) -> Self { RawData::Single(bytes) } } impl<'a> fmt::Debug for RawData<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { match *self { RawData::Single(buffer) => write!(fmt, "RawData::Single( [u8; {}] )", buffer.len()), RawData::Split(left, right) => write!( fmt, "RawData::Split( [u8; {}], [u8; {}] )", left.len(), right.len() ), } } } impl<'a> RawData<'a> { #[inline] pub fn empty() -> Self { RawData::Single(&[]) } pub fn read_exact(&mut self, buf: &mut [u8]) -> Result<(), std::io::Error> { let buf_len = buf.len(); *self = match *self { RawData::Single(single) => { if single.len() < buf_len { return Err(std::io::ErrorKind::UnexpectedEof.into()); } buf.copy_from_slice(&single[..buf_len]); RawData::Single(&single[buf_len..]) } RawData::Split(left, right) => { let left_len = left.len(); if buf_len <= left_len { buf.copy_from_slice(&left[..buf_len]); if buf_len < left_len { RawData::Split(&left[buf_len..], right) } else { RawData::Single(right) } } else { let remainder_len = buf_len - left_len; if remainder_len > right.len() { return Err(std::io::ErrorKind::UnexpectedEof.into()); } buf[..left_len].copy_from_slice(left); buf[left_len..].copy_from_slice(&right[..remainder_len]); RawData::Single(&right[remainder_len..]) } } }; Ok(()) } pub fn read_u64(&mut self) -> Result { let mut b = [0; 8]; self.read_exact(&mut b)?; Ok(T::read_u64(&b)) } pub fn read_u32(&mut self) -> Result { let mut b = [0; 4]; self.read_exact(&mut b)?; Ok(T::read_u32(&b)) } pub fn read_i32(&mut self) -> Result { let mut b = [0; 4]; self.read_exact(&mut b)?; Ok(T::read_i32(&b)) } pub fn read_u16(&mut self) -> Result { let mut b = [0; 2]; self.read_exact(&mut b)?; Ok(T::read_u16(&b)) } pub fn read_u8(&mut self) -> Result { let mut b = [0; 1]; self.read_exact(&mut b)?; Ok(b[0]) } /// Finds the first nul byte. Returns everything before that nul byte. /// Sets self to everything after the nul byte. pub fn read_string(&mut self) -> Option> { let (rv, new_self) = match *self { RawData::Single(single) => { let n = memchr::memchr(0, single)?; ( RawData::Single(&single[..n]), RawData::Single(&single[n + 1..]), ) } RawData::Split(left, right) => { if let Some(n) = memchr::memchr(0, left) { ( RawData::Single(&left[..n]), if n + 1 < left.len() { RawData::Split(&left[n + 1..], right) } else { RawData::Single(right) }, ) } else if let Some(n) = memchr::memchr(0, right) { ( RawData::Split(left, &right[..n]), RawData::Single(&right[n + 1..]), ) } else { return None; } } }; *self = new_self; Some(rv) } /// Returns the first `n` bytes, and sets self to the remainder. pub fn split_off_prefix(&mut self, n: usize) -> Result { let (rv, new_self) = match *self { RawData::Single(single) => { if single.len() < n { return Err(std::io::ErrorKind::UnexpectedEof.into()); } (RawData::Single(&single[..n]), RawData::Single(&single[n..])) } RawData::Split(left, right) => { if n <= left.len() { ( RawData::Single(&left[..n]), if n < left.len() { RawData::Split(&left[n..], right) } else { RawData::Single(right) }, ) } else { let remainder_len = n - left.len(); if remainder_len > right.len() { return Err(std::io::ErrorKind::UnexpectedEof.into()); } ( RawData::Split(left, &right[..remainder_len]), RawData::Single(&right[remainder_len..]), ) } } }; *self = new_self; Ok(rv) } pub fn skip(&mut self, n: usize) -> Result<(), std::io::Error> { *self = match *self { RawData::Single(single) => { if single.len() < n { return Err(std::io::ErrorKind::UnexpectedEof.into()); } RawData::Single(&single[n..]) } RawData::Split(left, right) => { if n < left.len() { RawData::Split(&left[n..], right) } else { let remainder_len = n - left.len(); if remainder_len > right.len() { return Err(std::io::ErrorKind::UnexpectedEof.into()); } RawData::Single(&right[remainder_len..]) } } }; Ok(()) } #[inline] fn write_into(&self, target: &mut Vec) { target.clear(); match *self { RawData::Single(slice) => target.extend_from_slice(slice), RawData::Split(first, second) => { target.reserve(first.len() + second.len()); target.extend_from_slice(first); target.extend_from_slice(second); } } } pub fn as_slice(&self) -> Cow<'a, [u8]> { match *self { RawData::Single(buffer) => buffer.into(), RawData::Split(..) => { let mut vec = Vec::new(); self.write_into(&mut vec); vec.into() } } } pub fn get(&self, range: Range) -> Option> { Some(match self { RawData::Single(buffer) => RawData::Single(buffer.get(range)?), RawData::Split(left, right) => { if range.start >= left.len() { RawData::Single(right.get(range.start - left.len()..range.end - left.len())?) } else if range.end <= left.len() { RawData::Single(left.get(range)?) } else { let left = left.get(range.start..)?; let right = right.get(..min(range.end - left.len(), right.len()))?; RawData::Split(left, right) } } }) } pub fn is_empty(&self) -> bool { match *self { RawData::Single(buffer) => buffer.is_empty(), RawData::Split(left, right) => left.is_empty() && right.is_empty(), } } pub fn len(&self) -> usize { match *self { RawData::Single(buffer) => buffer.len(), RawData::Split(left, right) => left.len() + right.len(), } } } #[derive(Clone, Copy, PartialEq, Eq)] pub struct RawDataU64<'a> { swapped_endian: bool, raw_data: RawData<'a>, } pub fn is_swapped_endian() -> bool { let mut buf = [0; 2]; T::write_u16(&mut buf, 0x1234); u16::from_ne_bytes(buf) != 0x1234 } impl<'a> RawDataU64<'a> { #[inline] pub fn from_raw_data(raw_data: RawData<'a>) -> Self { RawDataU64 { raw_data, swapped_endian: is_swapped_endian::(), } } pub fn is_empty(&self) -> bool { self.raw_data.is_empty() } pub fn len(&self) -> usize { self.raw_data.len() / mem::size_of::() } pub fn get(&self, index: usize) -> Option { let offset = index * mem::size_of::(); let mut data = self.raw_data; data.skip(offset).ok()?; let value = data.read_u64::().ok()?; Some(if self.swapped_endian { value.swap_bytes() } else { value }) } } impl<'a> std::fmt::Debug for RawDataU64<'a> { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { let mut list = fmt.debug_list(); let mut data = self.raw_data; while let Ok(value) = data.read_u64::() { let value = if self.swapped_endian { value.swap_bytes() } else { value }; list.entry(&HexValue(value)); } list.finish() } } #[cfg(test)] mod test { use super::RawData; #[test] fn test_reading_from_split() { let full = b"CDEF===AB"; // 0123___78" assert_eq!(full.len(), 9); let mut split = RawData::Split(&full[7..9], &full[0..4]); let mut dest = vec![0; 6]; split.read_exact(&mut dest).unwrap(); assert_eq!(&dest, b"ABCDEF"); } } linux-perf-event-reader-0.10.0/src/registers.rs000064400000000000000000000012021046102023000175110ustar 00000000000000use crate::RawDataU64; #[derive(Debug, Clone, PartialEq, Eq)] pub struct Regs<'a> { regs_mask: u64, raw_regs: RawDataU64<'a>, } impl<'a> Regs<'a> { pub fn new(regs_mask: u64, raw_regs: RawDataU64<'a>) -> Self { Self { regs_mask, raw_regs, } } pub fn get(&self, register: u64) -> Option { if self.regs_mask & (1 << register) == 0 { return None; } let mut index = 0; for i in 0..register { if self.regs_mask & (1 << i) != 0 { index += 1; } } self.raw_regs.get(index) } } linux-perf-event-reader-0.10.0/src/sample.rs000064400000000000000000000202711046102023000167720ustar 00000000000000use byteorder::ByteOrder; use crate::{BranchSampleFormat, CpuMode, RawData, RawDataU64, ReadFormat, SampleFormat}; use super::{RecordParseInfo, Regs}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct SampleRecord<'a> { pub id: Option, pub addr: Option, pub stream_id: Option, pub raw: Option>, pub ip: Option, pub timestamp: Option, pub pid: Option, pub tid: Option, pub cpu: Option, pub period: Option, pub user_regs: Option>, pub user_stack: Option<(RawData<'a>, u64)>, pub callchain: Option>, pub phys_addr: Option, pub data_page_size: Option, pub code_page_size: Option, pub intr_regs: Option>, pub cpu_mode: CpuMode, } impl<'a> SampleRecord<'a> { pub fn parse( data: RawData<'a>, misc: u16, parse_info: &RecordParseInfo, ) -> Result { let sample_format = parse_info.sample_format; let branch_sample_format = parse_info.branch_sample_format; let read_format = parse_info.read_format; let sample_regs_user = parse_info.sample_regs_user; let user_regs_count = parse_info.user_regs_count; let sample_regs_intr = parse_info.sample_regs_intr; let intr_regs_count = parse_info.intr_regs_count; let cpu_mode = CpuMode::from_misc(misc); let mut cur = data; let identifier = if sample_format.contains(SampleFormat::IDENTIFIER) { Some(cur.read_u64::()?) } else { None }; let ip = if sample_format.contains(SampleFormat::IP) { Some(cur.read_u64::()?) } else { None }; let (pid, tid) = if sample_format.contains(SampleFormat::TID) { let pid = cur.read_i32::()?; let tid = cur.read_i32::()?; (Some(pid), Some(tid)) } else { (None, None) }; let timestamp = if sample_format.contains(SampleFormat::TIME) { Some(cur.read_u64::()?) } else { None }; let addr = if sample_format.contains(SampleFormat::ADDR) { Some(cur.read_u64::()?) } else { None }; let id = if sample_format.contains(SampleFormat::ID) { Some(cur.read_u64::()?) } else { None }; let id = identifier.or(id); let stream_id = if sample_format.contains(SampleFormat::STREAM_ID) { Some(cur.read_u64::()?) } else { None }; let cpu = if sample_format.contains(SampleFormat::CPU) { let cpu = cur.read_u32::()?; let _reserved = cur.read_u32::()?; Some(cpu) } else { None }; let period = if sample_format.contains(SampleFormat::PERIOD) { let period = cur.read_u64::()?; Some(period) } else { None }; if sample_format.contains(SampleFormat::READ) { if read_format.contains(ReadFormat::GROUP) { let _value = cur.read_u64::()?; if read_format.contains(ReadFormat::TOTAL_TIME_ENABLED) { let _time_enabled = cur.read_u64::()?; } if read_format.contains(ReadFormat::TOTAL_TIME_RUNNING) { let _time_running = cur.read_u64::()?; } if read_format.contains(ReadFormat::ID) { let _id = cur.read_u64::()?; } } else { let nr = cur.read_u64::()?; if read_format.contains(ReadFormat::TOTAL_TIME_ENABLED) { let _time_enabled = cur.read_u64::()?; } if read_format.contains(ReadFormat::TOTAL_TIME_RUNNING) { let _time_running = cur.read_u64::()?; } for _ in 0..nr { let _value = cur.read_u64::()?; if read_format.contains(ReadFormat::ID) { let _id = cur.read_u64::()?; } } } } let callchain = if sample_format.contains(SampleFormat::CALLCHAIN) { let callchain_length = cur.read_u64::()?; let callchain = cur.split_off_prefix(callchain_length as usize * std::mem::size_of::())?; Some(RawDataU64::from_raw_data::(callchain)) } else { None }; let raw = if sample_format.contains(SampleFormat::RAW) { let size = cur.read_u32::()?; Some(cur.split_off_prefix(size as usize)?) } else { None }; if sample_format.contains(SampleFormat::BRANCH_STACK) { let nr = cur.read_u64::()?; if branch_sample_format.contains(BranchSampleFormat::HW_INDEX) { let _hw_idx = cur.read_u64::()?; } for _ in 0..nr { let _from = cur.read_u64::()?; let _to = cur.read_u64::()?; let _flags = cur.read_u64::()?; } } let user_regs = if sample_format.contains(SampleFormat::REGS_USER) { let regs_abi = cur.read_u64::()?; if regs_abi == 0 { None } else { let regs_data = cur.split_off_prefix(user_regs_count as usize * std::mem::size_of::())?; let raw_regs = RawDataU64::from_raw_data::(regs_data); let user_regs = Regs::new(sample_regs_user, raw_regs); Some(user_regs) } } else { None }; let user_stack = if sample_format.contains(SampleFormat::STACK_USER) { let stack_size = cur.read_u64::()?; let stack = cur.split_off_prefix(stack_size as usize)?; let dynamic_size = if stack_size != 0 { cur.read_u64::()? } else { 0 }; Some((stack, dynamic_size)) } else { None }; if sample_format.contains(SampleFormat::WEIGHT) { let _weight = cur.read_u64::()?; } if sample_format.contains(SampleFormat::DATA_SRC) { let _data_src = cur.read_u64::()?; } if sample_format.contains(SampleFormat::TRANSACTION) { let _transaction = cur.read_u64::()?; } let intr_regs = if sample_format.contains(SampleFormat::REGS_INTR) { let regs_abi = cur.read_u64::()?; if regs_abi == 0 { None } else { let regs_data = cur.split_off_prefix(intr_regs_count as usize * std::mem::size_of::())?; let raw_regs = RawDataU64::from_raw_data::(regs_data); let intr_regs = Regs::new(sample_regs_intr, raw_regs); Some(intr_regs) } } else { None }; let phys_addr = if sample_format.contains(SampleFormat::PHYS_ADDR) { Some(cur.read_u64::()?) } else { None }; if sample_format.contains(SampleFormat::AUX) { let size = cur.read_u64::()?; cur.skip(size as usize)?; } let data_page_size = if sample_format.contains(SampleFormat::DATA_PAGE_SIZE) { Some(cur.read_u64::()?) } else { None }; let code_page_size = if sample_format.contains(SampleFormat::CODE_PAGE_SIZE) { Some(cur.read_u64::()?) } else { None }; Ok(Self { id, ip, addr, stream_id, raw, user_regs, user_stack, callchain, cpu, timestamp, pid, tid, period, intr_regs, phys_addr, data_page_size, code_page_size, cpu_mode, }) } } linux-perf-event-reader-0.10.0/src/types.rs000064400000000000000000000331011046102023000166510ustar 00000000000000use crate::constants::*; use bitflags::bitflags; bitflags! { #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct SampleFormat: u64 { const IP = PERF_SAMPLE_IP; const TID = PERF_SAMPLE_TID; const TIME = PERF_SAMPLE_TIME; const ADDR = PERF_SAMPLE_ADDR; const READ = PERF_SAMPLE_READ; const CALLCHAIN = PERF_SAMPLE_CALLCHAIN; const ID = PERF_SAMPLE_ID; const CPU = PERF_SAMPLE_CPU; const PERIOD = PERF_SAMPLE_PERIOD; const STREAM_ID = PERF_SAMPLE_STREAM_ID; const RAW = PERF_SAMPLE_RAW; const BRANCH_STACK = PERF_SAMPLE_BRANCH_STACK; const REGS_USER = PERF_SAMPLE_REGS_USER; const STACK_USER = PERF_SAMPLE_STACK_USER; const WEIGHT = PERF_SAMPLE_WEIGHT; const DATA_SRC = PERF_SAMPLE_DATA_SRC; const IDENTIFIER = PERF_SAMPLE_IDENTIFIER; const TRANSACTION = PERF_SAMPLE_TRANSACTION; const REGS_INTR = PERF_SAMPLE_REGS_INTR; const PHYS_ADDR = PERF_SAMPLE_PHYS_ADDR; const AUX = PERF_SAMPLE_AUX; const CGROUP = PERF_SAMPLE_CGROUP; const DATA_PAGE_SIZE = PERF_SAMPLE_DATA_PAGE_SIZE; const CODE_PAGE_SIZE = PERF_SAMPLE_CODE_PAGE_SIZE; const WEIGHT_STRUCT = PERF_SAMPLE_WEIGHT_STRUCT; } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct BranchSampleFormat: u64 { /// user branches const USER = PERF_SAMPLE_BRANCH_USER; /// kernel branches const KERNEL = PERF_SAMPLE_BRANCH_KERNEL; /// hypervisor branches const HV = PERF_SAMPLE_BRANCH_HV; /// any branch types const ANY = PERF_SAMPLE_BRANCH_ANY; /// any call branch const ANY_CALL = PERF_SAMPLE_BRANCH_ANY_CALL; /// any return branch const ANY_RETURN = PERF_SAMPLE_BRANCH_ANY_RETURN; /// indirect calls const IND_CALL = PERF_SAMPLE_BRANCH_IND_CALL; /// transaction aborts const ABORT_TX = PERF_SAMPLE_BRANCH_ABORT_TX; /// in transaction const IN_TX = PERF_SAMPLE_BRANCH_IN_TX; /// not in transaction const NO_TX = PERF_SAMPLE_BRANCH_NO_TX; /// conditional branches const COND = PERF_SAMPLE_BRANCH_COND; /// call/ret stack const CALL_STACK = PERF_SAMPLE_BRANCH_CALL_STACK; /// indirect jumps const IND_JUMP = PERF_SAMPLE_BRANCH_IND_JUMP; /// direct call const CALL = PERF_SAMPLE_BRANCH_CALL; /// no flags const NO_FLAGS = PERF_SAMPLE_BRANCH_NO_FLAGS; /// no cycles const NO_CYCLES = PERF_SAMPLE_BRANCH_NO_CYCLES; /// save branch type const TYPE_SAVE = PERF_SAMPLE_BRANCH_TYPE_SAVE; /// save low level index of raw branch records const HW_INDEX = PERF_SAMPLE_BRANCH_HW_INDEX; } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct AttrFlags: u64 { /// off by default const DISABLED = ATTR_FLAG_BIT_DISABLED; /// children inherit it const INHERIT = ATTR_FLAG_BIT_INHERIT; /// must always be on PMU const PINNED = ATTR_FLAG_BIT_PINNED; /// only group on PMU const EXCLUSIVE = ATTR_FLAG_BIT_EXCLUSIVE; /// don't count user const EXCLUDE_USER = ATTR_FLAG_BIT_EXCLUDE_USER; /// don't count kernel const EXCLUDE_KERNEL = ATTR_FLAG_BIT_EXCLUDE_KERNEL; /// don't count hypervisor const EXCLUDE_HV = ATTR_FLAG_BIT_EXCLUDE_HV; /// don't count when idle const EXCLUDE_IDLE = ATTR_FLAG_BIT_EXCLUDE_IDLE; /// include mmap data const MMAP = ATTR_FLAG_BIT_MMAP; /// include comm data const COMM = ATTR_FLAG_BIT_COMM; /// use freq, not period const FREQ = ATTR_FLAG_BIT_FREQ; /// per task counts const INHERIT_STAT = ATTR_FLAG_BIT_INHERIT_STAT; /// next exec enables const ENABLE_ON_EXEC = ATTR_FLAG_BIT_ENABLE_ON_EXEC; /// trace fork/exit const TASK = ATTR_FLAG_BIT_TASK; /// wakeup_watermark const WATERMARK = ATTR_FLAG_BIT_WATERMARK; /// one of the two PRECISE_IP bitmask bits const PRECISE_IP_BIT_15 = 1 << 15; /// one of the two PRECISE_IP bitmask bits const PRECISE_IP_BIT_16 = 1 << 16; /// the full PRECISE_IP bitmask const PRECISE_IP_BITMASK = ATTR_FLAG_BITMASK_PRECISE_IP; /// non-exec mmap data const MMAP_DATA = ATTR_FLAG_BIT_MMAP_DATA; /// sample_type all events const SAMPLE_ID_ALL = ATTR_FLAG_BIT_SAMPLE_ID_ALL; /// don't count in host const EXCLUDE_HOST = ATTR_FLAG_BIT_EXCLUDE_HOST; /// don't count in guest const EXCLUDE_GUEST = ATTR_FLAG_BIT_EXCLUDE_GUEST; /// exclude kernel callchains const EXCLUDE_CALLCHAIN_KERNEL = ATTR_FLAG_BIT_EXCLUDE_CALLCHAIN_KERNEL; /// exclude user callchains const EXCLUDE_CALLCHAIN_USER = ATTR_FLAG_BIT_EXCLUDE_CALLCHAIN_USER; /// include mmap with inode data const MMAP2 = ATTR_FLAG_BIT_MMAP2; /// flag comm events that are due to exec const COMM_EXEC = ATTR_FLAG_BIT_COMM_EXEC; /// use @clockid for time fields const USE_CLOCKID = ATTR_FLAG_BIT_USE_CLOCKID; /// context switch data const CONTEXT_SWITCH = ATTR_FLAG_BIT_CONTEXT_SWITCH; /// Write ring buffer from end to beginning const WRITE_BACKWARD = ATTR_FLAG_BIT_WRITE_BACKWARD; /// include namespaces data const NAMESPACES = ATTR_FLAG_BIT_NAMESPACES; /// include ksymbol events const KSYMBOL = ATTR_FLAG_BIT_KSYMBOL; /// include bpf events const BPF_EVENT = ATTR_FLAG_BIT_BPF_EVENT; /// generate AUX records instead of events const AUX_OUTPUT = ATTR_FLAG_BIT_AUX_OUTPUT; /// include cgroup events const CGROUP = ATTR_FLAG_BIT_CGROUP; /// include text poke events const TEXT_POKE = ATTR_FLAG_BIT_TEXT_POKE; /// use build id in mmap2 events const BUILD_ID = ATTR_FLAG_BIT_BUILD_ID; /// children only inherit if cloned with CLONE_THREAD const INHERIT_THREAD = ATTR_FLAG_BIT_INHERIT_THREAD; /// event is removed from task on exec const REMOVE_ON_EXEC = ATTR_FLAG_BIT_REMOVE_ON_EXEC; /// send synchronous SIGTRAP on event const SIGTRAP = ATTR_FLAG_BIT_SIGTRAP; } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct HwBreakpointType: u32 { /// No breakpoint. (`HW_BREAKPOINT_EMPTY`) const EMPTY = 0; /// Count when we read the memory location. (`HW_BREAKPOINT_R`) const R = 1; /// Count when we write the memory location. (`HW_BREAKPOINT_W`) const W = 2; /// Count when we read or write the memory location. (`HW_BREAKPOINT_RW`) const RW = Self::R.bits() | Self::W.bits(); /// Count when we execute code at the memory location. (`HW_BREAKPOINT_X`) const X = 4; /// The combination of `HW_BREAKPOINT_R` or `HW_BREAKPOINT_W` with //// `HW_BREAKPOINT_X` is not allowed. (`HW_BREAKPOINT_INVALID`) const INVALID = Self::RW.bits() | Self::X.bits(); } /// The format of the data returned by read() on a perf event fd, /// as specified by attr.read_format: /// /// ```pseudo-c /// 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 /// }; /// ``` #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct ReadFormat: u64 { const TOTAL_TIME_ENABLED = PERF_FORMAT_TOTAL_TIME_ENABLED; const TOTAL_TIME_RUNNING = PERF_FORMAT_TOTAL_TIME_RUNNING; const ID = PERF_FORMAT_ID; const GROUP = PERF_FORMAT_GROUP; } } /// Specifies how precise the instruction address should be. /// With `perf record -e` you can set the precision by appending /p to the /// event name, with varying numbers of `p`s. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum IpSkidConstraint { /// 0 - SAMPLE_IP can have arbitrary skid ArbitrarySkid, /// 1 - SAMPLE_IP must have constant skid ConstantSkid, /// 2 - SAMPLE_IP requested to have 0 skid ZeroSkid, /// 3 - SAMPLE_IP must have 0 skid, or uses randomization to avoid /// sample shadowing effects. ZeroSkidOrRandomization, } impl AttrFlags { /// Extract the IpSkidConstraint from the bits. pub fn ip_skid_constraint(&self) -> IpSkidConstraint { match (self.bits() & Self::PRECISE_IP_BITMASK.bits()) >> 15 { 0 => IpSkidConstraint::ArbitrarySkid, 1 => IpSkidConstraint::ConstantSkid, 2 => IpSkidConstraint::ZeroSkid, 3 => IpSkidConstraint::ZeroSkidOrRandomization, _ => unreachable!(), } } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum ClockId { Realtime, Monotonic, ProcessCputimeId, ThreadCputimeId, MonotonicRaw, RealtimeCoarse, MonotonicCoarse, Boottime, RealtimeAlarm, BoottimeAlarm, } impl ClockId { pub fn from_u32(clockid: u32) -> Option { Some(match clockid { 0 => Self::Realtime, 1 => Self::Monotonic, 2 => Self::ProcessCputimeId, 3 => Self::ThreadCputimeId, 4 => Self::MonotonicRaw, 5 => Self::RealtimeCoarse, 6 => Self::MonotonicCoarse, 7 => Self::Boottime, 8 => Self::RealtimeAlarm, 9 => Self::BoottimeAlarm, _ => return None, }) } } #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct RecordType(pub u32); impl RecordType { // Kernel-built-in record types pub const MMAP: Self = Self(PERF_RECORD_MMAP); pub const LOST: Self = Self(PERF_RECORD_LOST); pub const COMM: Self = Self(PERF_RECORD_COMM); pub const EXIT: Self = Self(PERF_RECORD_EXIT); pub const THROTTLE: Self = Self(PERF_RECORD_THROTTLE); pub const UNTHROTTLE: Self = Self(PERF_RECORD_UNTHROTTLE); pub const FORK: Self = Self(PERF_RECORD_FORK); pub const READ: Self = Self(PERF_RECORD_READ); pub const SAMPLE: Self = Self(PERF_RECORD_SAMPLE); pub const MMAP2: Self = Self(PERF_RECORD_MMAP2); pub const AUX: Self = Self(PERF_RECORD_AUX); pub const ITRACE_START: Self = Self(PERF_RECORD_ITRACE_START); pub const LOST_SAMPLES: Self = Self(PERF_RECORD_LOST_SAMPLES); pub const SWITCH: Self = Self(PERF_RECORD_SWITCH); pub const SWITCH_CPU_WIDE: Self = Self(PERF_RECORD_SWITCH_CPU_WIDE); pub const NAMESPACES: Self = Self(PERF_RECORD_NAMESPACES); pub const KSYMBOL: Self = Self(PERF_RECORD_KSYMBOL); pub const BPF_EVENT: Self = Self(PERF_RECORD_BPF_EVENT); pub const CGROUP: Self = Self(PERF_RECORD_CGROUP); pub const TEXT_POKE: Self = Self(PERF_RECORD_TEXT_POKE); pub const AUX_OUTPUT_HW_ID: Self = Self(PERF_RECORD_AUX_OUTPUT_HW_ID); pub fn is_builtin_type(&self) -> bool { self.0 < PERF_RECORD_USER_TYPE_START } pub fn is_user_type(&self) -> bool { self.0 >= PERF_RECORD_USER_TYPE_START } } impl std::fmt::Debug for RecordType { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { let s = match *self { Self::MMAP => "MMAP", Self::LOST => "LOST", Self::COMM => "COMM", Self::EXIT => "EXIT", Self::THROTTLE => "THROTTLE", Self::UNTHROTTLE => "UNTHROTTLE", Self::FORK => "FORK", Self::READ => "READ", Self::SAMPLE => "SAMPLE", Self::MMAP2 => "MMAP2", Self::AUX => "AUX", Self::ITRACE_START => "ITRACE_START", Self::LOST_SAMPLES => "LOST_SAMPLES", Self::SWITCH => "SWITCH", Self::SWITCH_CPU_WIDE => "SWITCH_CPU_WIDE", Self::NAMESPACES => "NAMESPACES", Self::KSYMBOL => "KSYMBOL", Self::BPF_EVENT => "BPF_EVENT", Self::CGROUP => "CGROUP", Self::TEXT_POKE => "TEXT_POKE", Self::AUX_OUTPUT_HW_ID => "AUX_OUTPUT_HW_ID", other if self.is_builtin_type() => { return fmt.write_fmt(format_args!("Unknown built-in: {}", other.0)); } other => { return fmt.write_fmt(format_args!("User type: {}", other.0)); } }; fmt.write_str(s) } } #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[non_exhaustive] pub enum CpuMode { Unknown, Kernel, User, Hypervisor, GuestKernel, GuestUser, } impl CpuMode { /// Initialize from the misc field of the perf event header. pub fn from_misc(misc: u16) -> Self { match misc & PERF_RECORD_MISC_CPUMODE_MASK { PERF_RECORD_MISC_CPUMODE_UNKNOWN => Self::Unknown, PERF_RECORD_MISC_KERNEL => Self::Kernel, PERF_RECORD_MISC_USER => Self::User, PERF_RECORD_MISC_HYPERVISOR => Self::Hypervisor, PERF_RECORD_MISC_GUEST_KERNEL => Self::GuestKernel, PERF_RECORD_MISC_GUEST_USER => Self::GuestUser, _ => Self::Unknown, } } } linux-perf-event-reader-0.10.0/src/utils.rs000064400000000000000000000007261046102023000166540ustar 00000000000000use std::fmt; pub struct HexValue(pub u64); impl fmt::Debug for HexValue { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { write!(fmt, "0x{:016X}", self.0) } } pub struct HexSlice<'a>(pub &'a [u64]); impl<'a> fmt::Debug for HexSlice<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> { fmt.debug_list() .entries(self.0.iter().map(|&value| HexValue(value))) .finish() } }