diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/perf-event | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/perf-event')
-rw-r--r-- | vendor/perf-event/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/perf-event/Cargo.lock | 24 | ||||
-rw-r--r-- | vendor/perf-event/Cargo.toml | 26 | ||||
-rw-r--r-- | vendor/perf-event/LICENSE-APACHE | 201 | ||||
-rw-r--r-- | vendor/perf-event/LICENSE-MIT | 23 | ||||
-rw-r--r-- | vendor/perf-event/README.md | 45 | ||||
-rw-r--r-- | vendor/perf-event/TODO.org | 2 | ||||
-rwxr-xr-x | vendor/perf-event/coverage.sh | 17 | ||||
-rw-r--r-- | vendor/perf-event/examples/big-group.rs | 72 | ||||
-rw-r--r-- | vendor/perf-event/examples/group.rs | 45 | ||||
-rw-r--r-- | vendor/perf-event/examples/insns-for-pid.rs | 27 | ||||
-rw-r--r-- | vendor/perf-event/examples/println-cpi.rs | 22 | ||||
-rw-r--r-- | vendor/perf-event/examples/println.rs | 15 | ||||
-rw-r--r-- | vendor/perf-event/src/events.rs | 321 | ||||
-rw-r--r-- | vendor/perf-event/src/lib.rs | 1043 | ||||
-rw-r--r-- | vendor/perf-event/wrapper.h | 20 |
16 files changed, 1904 insertions, 0 deletions
diff --git a/vendor/perf-event/.cargo-checksum.json b/vendor/perf-event/.cargo-checksum.json new file mode 100644 index 000000000..6227f9408 --- /dev/null +++ b/vendor/perf-event/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.lock":"ffee78a24c58d4f3c188bf811ed237259da3d6e7e4985ea1537b049e95fa7882","Cargo.toml":"a1fb1bb3738b7261b434ce43382582029fca06afbffd107aefea5c7f33648dca","LICENSE-APACHE":"a60eea817514531668d7e00765731449fe14d059d3249e0bc93b36de45f759f2","LICENSE-MIT":"23f18e03dc49df91622fe2a76176497404e46ced8a715d9d2b67a7446571cca3","README.md":"c1344e75a67a817b375cbdc47acb63776fc3c73a1820cf4782306cae0840c4f1","TODO.org":"9bba41063f48330b4060ac37744495a8d5a03fefa15290a1fe3c783f78321477","coverage.sh":"499e0ba26d5b1402d2de552afde9c93a010055dbc534c21ed4a1bd19e4829f52","examples/big-group.rs":"0410be6da09fdd9d8f94420bd6f7e54fd15167c40738042fc1057ec1853af523","examples/group.rs":"88e608c1c9bae244cb6b418c6c70d2c89c98225791e98b043c9a20374e9bd280","examples/insns-for-pid.rs":"076b2b9bb5ab56d9a4b543088e9f4213f97cfcb18cf6fffa5ac9e511ca424edd","examples/println-cpi.rs":"1fae9a436af127314828f647ae846ab30683b70de8838d837ffff44e9c15a7a7","examples/println.rs":"98503038ba2bd155a76a440593ce082e5a176cdb479f865c843f4b960e4c815a","src/events.rs":"51d3b151633e938ceeabf863408890295cd8a8168b384f612d009c9b3c5cf4c4","src/lib.rs":"e099d00a7cec768429ae28769dd42ad812b90e3297eb8fa4af1bcbaf799a7277","wrapper.h":"2291357bf1a2dd75892322253f0a6fc93ea6cb0ba363e689bf0354dbda10d550"},"package":"5396562cd2eaa828445d6d34258ae21ee1eb9d40fe626ca7f51c8dccb4af9d66"}
\ No newline at end of file diff --git a/vendor/perf-event/Cargo.lock b/vendor/perf-event/Cargo.lock new file mode 100644 index 000000000..e807dc90f --- /dev/null +++ b/vendor/perf-event/Cargo.lock @@ -0,0 +1,24 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "libc" +version = "0.2.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2f02823cf78b754822df5f7f268fb59822e7296276d3e069d8e8cb26a14bd10" + +[[package]] +name = "perf-event" +version = "0.4.7" +dependencies = [ + "libc", + "perf-event-open-sys", +] + +[[package]] +name = "perf-event-open-sys" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66f37842e29d92d05872a3c0271ba6717842695ecb896cb2e147a825c804b207" +dependencies = [ + "libc", +] diff --git a/vendor/perf-event/Cargo.toml b/vendor/perf-event/Cargo.toml new file mode 100644 index 000000000..ed684cc4c --- /dev/null +++ b/vendor/perf-event/Cargo.toml @@ -0,0 +1,26 @@ +# 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 believe there's an error in this file please file an +# issue against the rust-lang/cargo repository. If you're +# editing this file be aware that the upstream Cargo.toml +# will likely look very different (and much more reasonable) + +[package] +edition = "2018" +name = "perf-event" +version = "0.4.7" +authors = ["Jim Blandy <jimb@red-bean.com>"] +description = "A Rust interface to Linux performance monitoring" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/jimblandy/perf-event.git" +[dependencies.libc] +version = "0.2" + +[dependencies.perf-event-open-sys] +version = "1.0" diff --git a/vendor/perf-event/LICENSE-APACHE b/vendor/perf-event/LICENSE-APACHE new file mode 100644 index 000000000..16fe87b06 --- /dev/null +++ b/vendor/perf-event/LICENSE-APACHE @@ -0,0 +1,201 @@ + 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. diff --git a/vendor/perf-event/LICENSE-MIT b/vendor/perf-event/LICENSE-MIT new file mode 100644 index 000000000..31aa79387 --- /dev/null +++ b/vendor/perf-event/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/vendor/perf-event/README.md b/vendor/perf-event/README.md new file mode 100644 index 000000000..9848ca226 --- /dev/null +++ b/vendor/perf-event/README.md @@ -0,0 +1,45 @@ +## perf-event: a Rust interface to Linux performance monitoring + +*This is a nascent project. Tests are lacking. The design may change.* + +This uses the Linux [`perf_event_open`][man] API to access performance monitoring +hardware and software. Use `Builder` to create a perf event counter, then use +`enable` and `disable` to start and stop counting. Call `read` to get your +count. + +For example, this counts the number of cycles used by the call to `println!`. +Try adjusting the length of the vector to see the cycle count change. + + use perf_event::Builder; + + fn main() -> std::io::Result<()> { + let mut counter = Builder::new().build()?; + + let vec = (0..=51).collect::<Vec<_>>(); + + counter.enable()?; + println!("{:?}", vec); + counter.disable()?; + + println!("{} instructions retired", counter.read()?); + + Ok(()) + } + +Since we don't specify what sort of event we want to count, `Builder` defaults +to `PERF_COUNT_HW_INSTRUCTIONS` events, whose documentation says: + +> Retired instructions. Be careful, these can be affected by various issues, +> most notably hardware interrupt counts. + +The `examples` directory includes programs that count other sorts of events. + +[man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html + +## See also + +The [`perfcnt`] crate provides more extensive coverage of the Linux +`perf_event_open` API than this crate. However, `perfcnt` does not build on +stable Rust. + +[`perfcnt`]: https://crates.io/crates/perfcnt diff --git a/vendor/perf-event/TODO.org b/vendor/perf-event/TODO.org new file mode 100644 index 000000000..24d7e53df --- /dev/null +++ b/vendor/perf-event/TODO.org @@ -0,0 +1,2 @@ +- write Counter::new; use in examples +- raw Event: just provide type, config, config1, config2 values, as read from sysfs diff --git a/vendor/perf-event/coverage.sh b/vendor/perf-event/coverage.sh new file mode 100755 index 000000000..de8d66051 --- /dev/null +++ b/vendor/perf-event/coverage.sh @@ -0,0 +1,17 @@ +#!/usr/bin/env bash + +cargo +nightly clean +export RUSTFLAGS="\ + -Zprofile \ + -Ccodegen-units=1 \ + -Copt-level=0 \ + -Clink-dead-code \ + -Coverflow-checks=off \ +" +export CARGO_INCREMENTAL=0 + +cargo +nightly test + +grcov ./target/debug/ -s . -t html --llvm --branch --ignore-not-existing -o ./target/debug/coverage/ + +xdg-open target/debug/coverage/index.html diff --git a/vendor/perf-event/examples/big-group.rs b/vendor/perf-event/examples/big-group.rs new file mode 100644 index 000000000..73b557827 --- /dev/null +++ b/vendor/perf-event/examples/big-group.rs @@ -0,0 +1,72 @@ +use perf_event::{Builder, Group}; +use perf_event::events::{Cache, CacheOp, CacheResult, Hardware, WhichCache}; + +fn main() -> std::io::Result<()> { + const ACCESS: Cache = Cache { + which: WhichCache::L1D, + operation: CacheOp::READ, + result: CacheResult::ACCESS, + }; + const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; + + let mut group = Group::new()?; + let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; + let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; + let branches = Builder::new().group(&mut group).kind(Hardware::BRANCH_INSTRUCTIONS).build()?; + let missed_branches = Builder::new().group(&mut group).kind(Hardware::BRANCH_MISSES).build()?; + let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; + let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; + + // Note that if you add more counters than you actually have hardware for, + // the kernel will time-slice them, which means you may get no coverage for + // short measurements. See the documentation. + // + // On my machine, this program won't collect any data unless I disable the + // NMI watchdog, as described in the documentation for `Group`. My machine + // has four counters, and this program tries to use all of them, but the NMI + // watchdog uses one up. + + let mut vec = (0..=100000).collect::<Vec<_>>(); + + group.enable()?; + vec.sort(); + println!("{:?}", &vec[0..10]); + group.disable()?; + + let counts = group.read()?; + + println!("enabled for {}ns, actually running for {}ns", + counts.time_enabled(), + counts.time_running()); + + if counts.time_running() == 0 { + println!("Group was never running; no results available."); + return Ok(()); + } + + if counts.time_running() < counts.time_enabled() { + println!("Counts cover only a portion of the execution."); + } + + println!("L1D cache misses/references: {} / {} ({:.0}%)", + counts[&miss_counter], + counts[&access_counter], + (counts[&miss_counter] as f64 / counts[&access_counter] as f64) * 100.0); + + println!("branch prediction misses/total: {} / {} ({:.0}%)", + counts[&missed_branches], + counts[&branches], + (counts[&missed_branches] as f64 / counts[&branches] as f64) * 100.0); + + println!("{} instructions, {} cycles ({:.2} cpi)", + counts[&insns], + counts[&cycles], + counts[&cycles] as f64 / counts[&insns] as f64); + + // You can iterate over a `Counts` value: + for (id, value) in &counts { + println!("Counter id {} has value {}", id, value); + } + + Ok(()) +} diff --git a/vendor/perf-event/examples/group.rs b/vendor/perf-event/examples/group.rs new file mode 100644 index 000000000..0312485c9 --- /dev/null +++ b/vendor/perf-event/examples/group.rs @@ -0,0 +1,45 @@ +use perf_event::{Builder, Group}; +use perf_event::events::{Cache, CacheOp, CacheResult, Hardware, WhichCache}; + +fn main() -> std::io::Result<()> { + const ACCESS: Cache = Cache { + which: WhichCache::L1D, + operation: CacheOp::READ, + result: CacheResult::ACCESS, + }; + const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; + + let mut group = Group::new()?; + let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; + let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; + let branches = Builder::new().group(&mut group).kind(Hardware::BRANCH_INSTRUCTIONS).build()?; + let missed_branches = Builder::new().group(&mut group).kind(Hardware::BRANCH_MISSES).build()?; + + // Note that if you add more counters than you actually have hardware for, + // the kernel will time-slice them, which means you may get no coverage for + // short measurements. See the documentation. + + let vec = (0..=51).collect::<Vec<_>>(); + + group.enable()?; + println!("{:?}", vec); + group.disable()?; + + let counts = group.read()?; + println!("L1D cache misses/references: {} / {} ({:.0}%)", + counts[&miss_counter], + counts[&access_counter], + (counts[&miss_counter] as f64 / counts[&access_counter] as f64) * 100.0); + + println!("branch prediction misses/total: {} / {} ({:.0}%)", + counts[&missed_branches], + counts[&branches], + (counts[&missed_branches] as f64 / counts[&branches] as f64) * 100.0); + + // You can iterate over a `Counts` value: + for (id, value) in &counts { + println!("Counter id {} has value {}", id, value); + } + + Ok(()) +} diff --git a/vendor/perf-event/examples/insns-for-pid.rs b/vendor/perf-event/examples/insns-for-pid.rs new file mode 100644 index 000000000..09d4ef36d --- /dev/null +++ b/vendor/perf-event/examples/insns-for-pid.rs @@ -0,0 +1,27 @@ +use libc::pid_t; +use perf_event::Builder; +use perf_event::events::Hardware; +use std::thread::sleep; +use std::time::Duration; + +fn main() -> std::io::Result<()> { + let pid: pid_t = std::env::args() + .nth(1) + .expect("Usage: insns-for-pid PID") + .parse() + .expect("Usage: insns-for-pid PID"); + + let mut insns = Builder::new() + .observe_pid(pid) + .kind(Hardware::BRANCH_INSTRUCTIONS) + .build()?; + + // Count instructions in PID for five seconds. + insns.enable()?; + sleep(Duration::from_secs(5)); + insns.disable()?; + + println!("instructions in last five seconds: {}", insns.read()?); + + Ok(()) +} diff --git a/vendor/perf-event/examples/println-cpi.rs b/vendor/perf-event/examples/println-cpi.rs new file mode 100644 index 000000000..31d24f640 --- /dev/null +++ b/vendor/perf-event/examples/println-cpi.rs @@ -0,0 +1,22 @@ +fn main() -> std::io::Result<()> { + use perf_event::{Builder, Group}; + use perf_event::events::Hardware; + + let mut group = Group::new()?; + let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; + let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; + + let vec = (0..=51).collect::<Vec<_>>(); + + group.enable()?; + println!("{:?}", vec); + group.disable()?; + + let counts = group.read()?; + println!("cycles / instructions: {} / {} ({:.2} cpi)", + counts[&cycles], + counts[&insns], + (counts[&cycles] as f64 / counts[&insns] as f64)); + + Ok(()) +} diff --git a/vendor/perf-event/examples/println.rs b/vendor/perf-event/examples/println.rs new file mode 100644 index 000000000..8760e3dcd --- /dev/null +++ b/vendor/perf-event/examples/println.rs @@ -0,0 +1,15 @@ +use perf_event::Builder; + +fn main() -> std::io::Result<()> { + let mut counter = Builder::new().build()?; + + let vec = (0..=51).collect::<Vec<_>>(); + + counter.enable()?; + println!("{:?}", vec); + counter.disable()?; + + println!("{} instructions retired", counter.read()?); + + Ok(()) +} diff --git a/vendor/perf-event/src/events.rs b/vendor/perf-event/src/events.rs new file mode 100644 index 000000000..1309ab0c7 --- /dev/null +++ b/vendor/perf-event/src/events.rs @@ -0,0 +1,321 @@ +//! Events we can monitor or count. +//! +//! There are three general categories of event: +//! +//! - [`Hardware`] events are counted by the processor itself. This +//! includes things like clock cycles, instructions retired, and cache and +//! branch prediction statistics. +//! +//! - [`Software`] events are counted by the kernel. This includes things +//! like context switches, page faults, and so on. +//! +//! - [`Cache`] events offer a more detailed view of the processor's cache +//! counters. You can select which level of the cache hierarchy to observe, +//! discriminate between data and instruction caches, and so on. +//! +//! The `Event` type is just an enum with a variant for each of the above types, +//! which all implement `Into<Event>`. +//! +//! Linux supports many more kinds of events than this module covers, including +//! events specific to particular make and model of processor, and events that +//! are dynamically registered by drivers and kernel modules. If something you +//! want is missing, think about the best API to expose it, and submit a pull +//! request! +//! +//! [`Hardware`]: enum.Hardware.html +//! [`Software`]: enum.Software.html +//! [`Cache`]: struct.Cache.html + +#![allow(non_camel_case_types)] +use perf_event_open_sys::bindings as bindings; + +/// Any sort of event. This is a sum of the [`Hardware`], +/// [`Software`], and [`Cache`] types, which all implement +/// `Into<Event>`. +/// +/// [`Hardware`]: enum.Hardware.html +/// [`Software`]: enum.Software.html +/// [`Cache`]: struct.Cache.html +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum Event { + #[allow(missing_docs)] + Hardware(Hardware), + + #[allow(missing_docs)] + Software(Software), + + #[allow(missing_docs)] + Cache(Cache), +} + +impl Event { + pub(crate) fn as_type(&self) -> bindings::perf_type_id { + match self { + Event::Hardware(_) => bindings::perf_type_id_PERF_TYPE_HARDWARE, + Event::Software(_) => bindings::perf_type_id_PERF_TYPE_SOFTWARE, + Event::Cache(_) => bindings::perf_type_id_PERF_TYPE_HW_CACHE, + } + } + + pub(crate) fn as_config(self) -> u64 { + match self { + Event::Hardware(hw) => hw as _, + Event::Software(sw) => sw as _, + Event::Cache(cache) => cache.as_config(), + } + } +} + +/// Hardware counters. +/// +/// These are counters implemented by the processor itself. Such counters vary +/// from one architecture to the next, and even different models within a +/// particular architecture will often change the way they expose this data. +/// This is a selection of portable names for values that can be obtained on a +/// wide variety of systems. +/// +/// Each variant of this enum corresponds to a particular `PERF_COUNT_HW_`... +/// value supported by the [`perf_event_open`][man] system call. +/// +/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Hardware { + /// Total cycles. Be wary of what happens during CPU frequency scaling. + CPU_CYCLES = bindings::perf_hw_id_PERF_COUNT_HW_CPU_CYCLES, + + /// Retired instructions. Be careful, these can be affected by various + /// issues, most notably hardware interrupt counts. + INSTRUCTIONS = bindings::perf_hw_id_PERF_COUNT_HW_INSTRUCTIONS, + + /// Cache accesses. Usually this indicates Last Level Cache accesses but + /// this may vary depending on your CPU. This may include prefetches and + /// coherency messages; again this depends on the design of your CPU. + CACHE_REFERENCES = bindings::perf_hw_id_PERF_COUNT_HW_CACHE_REFERENCES, + + /// Cache misses. Usually this indicates Last Level Cache misses; this is + /// intended to be used in conjunction with the + /// PERF_COUNT_HW_CACHE_REFERENCES event to calculate cache miss rates. + CACHE_MISSES = bindings::perf_hw_id_PERF_COUNT_HW_CACHE_MISSES, + + /// Retired branch instructions. Prior to Linux 2.6.35, this used the wrong + /// event on AMD processors. + BRANCH_INSTRUCTIONS = bindings::perf_hw_id_PERF_COUNT_HW_BRANCH_INSTRUCTIONS, + + /// Mispredicted branch instructions. + BRANCH_MISSES = bindings::perf_hw_id_PERF_COUNT_HW_BRANCH_MISSES, + + /// Bus cycles, which can be different from total cycles. + BUS_CYCLES = bindings::perf_hw_id_PERF_COUNT_HW_BUS_CYCLES, + + /// Stalled cycles during issue. (since Linux 3.0) + STALLED_CYCLES_FRONTEND = bindings::perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_FRONTEND, + + /// Stalled cycles during retirement. (since Linux 3.0) + STALLED_CYCLES_BACKEND = bindings::perf_hw_id_PERF_COUNT_HW_STALLED_CYCLES_BACKEND, + + /// Total cycles; not affected by CPU frequency scaling. (since Linux 3.3) + REF_CPU_CYCLES = bindings::perf_hw_id_PERF_COUNT_HW_REF_CPU_CYCLES, +} + +impl From<Hardware> for Event { + fn from(hw: Hardware) -> Event { + Event::Hardware(hw) + } +} + +/// Software counters, implemented by the kernel. +/// +/// Each variant of this enum corresponds to a particular `PERF_COUNT_SW_`... +/// value supported by the [`perf_event_open`][man] system call. +/// +/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +#[repr(u32)] +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum Software { + /// This reports the CPU clock, a high-resolution per-CPU timer. + CPU_CLOCK = bindings::perf_sw_ids_PERF_COUNT_SW_CPU_CLOCK, + + /// This reports a clock count specific to the task that is running. + TASK_CLOCK = bindings::perf_sw_ids_PERF_COUNT_SW_TASK_CLOCK, + + /// This reports the number of page faults. + PAGE_FAULTS = bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS, + + /// This counts context switches. Until Linux 2.6.34, these were all + /// reported as user-space events, after that they are reported as happening + /// in the kernel. + CONTEXT_SWITCHES = bindings::perf_sw_ids_PERF_COUNT_SW_CONTEXT_SWITCHES, + + /// This reports the number of times the process has migrated to a new CPU. + CPU_MIGRATIONS = bindings::perf_sw_ids_PERF_COUNT_SW_CPU_MIGRATIONS, + + /// This counts the number of minor page faults. These did not require disk + /// I/O to handle. + PAGE_FAULTS_MIN = bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MIN, + + /// This counts the number of major page faults. These required disk I/O to + /// handle. + PAGE_FAULTS_MAJ = bindings::perf_sw_ids_PERF_COUNT_SW_PAGE_FAULTS_MAJ, + + /// (since Linux 2.6.33) This counts the number of alignment faults. These + /// happen when unaligned memory accesses happen; the kernel can handle + /// these but it reduces performance. This happens only on some + /// architectures (never on x86). + ALIGNMENT_FAULTS = bindings::perf_sw_ids_PERF_COUNT_SW_ALIGNMENT_FAULTS, + + /// (since Linux 2.6.33) This counts the number of emulation faults. The + /// kernel sometimes traps on unimplemented instructions and emulates them + /// for user space. This can negatively impact performance. + EMULATION_FAULTS = bindings::perf_sw_ids_PERF_COUNT_SW_EMULATION_FAULTS, + + /// (since Linux 3.12) This is a placeholder event that counts nothing. + /// Informational sample record types such as mmap or comm must be + /// associated with an active event. This dummy event allows gathering such + /// records without requiring a counting event. + DUMMY = bindings::perf_sw_ids_PERF_COUNT_SW_DUMMY, +} + +impl From<Software> for Event { + fn from(hw: Software) -> Event { + Event::Software(hw) + } +} + +/// A cache event. +/// +/// A cache event has three identifying characteristics: +/// +/// - which cache to observe ([`which`]) +/// +/// - what sort of request it's handling ([`operation`]) +/// +/// - whether we want to count all cache accesses, or just misses +/// ([`result`]). +/// +/// For example, to measure the L1 data cache's miss rate: +/// +/// # use perf_event::{Builder, Group}; +/// # use perf_event::events::{Cache, CacheOp, CacheResult, Hardware, WhichCache}; +/// # fn main() -> std::io::Result<()> { +/// // A `Cache` value representing L1 data cache read accesses. +/// const ACCESS: Cache = Cache { +/// which: WhichCache::L1D, +/// operation: CacheOp::READ, +/// result: CacheResult::ACCESS, +/// }; +/// +/// // A `Cache` value representing L1 data cache read misses. +/// const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; +/// +/// // Construct a `Group` containing the two new counters, from which we +/// // can get counts over matching periods of time. +/// let mut group = Group::new()?; +/// let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; +/// let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; +/// # Ok(()) } +/// +/// [`which`]: enum.WhichCache.html +/// [`operation`]: enum.CacheOp.html +/// [`result`]: enum.CacheResult.html +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct Cache { + /// Which cache is being monitored? (data, instruction, ...) + pub which: WhichCache, + + /// What operation is being monitored? (read, write, etc.) + pub operation: CacheOp, + + /// All accesses, or just misses? + pub result: CacheResult, +} + +impl From<Cache> for Event { + fn from(hw: Cache) -> Event { + Event::Cache(hw) + } +} + +impl Cache { + fn as_config(&self) -> u64 { + self.which as u64 | + ((self.operation as u64) << 8) | + ((self.result as u64) << 16) + } +} + +/// A cache whose events we would like to count. +/// +/// This is used in the `Cache` type as part of the identification of a cache +/// event. Each variant here corresponds to a particular +/// `PERF_COUNT_HW_CACHE_...` constant supported by the [`perf_event_open`][man] +/// system call. +/// +/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +#[repr(u32)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum WhichCache { + /// for measuring Level 1 Data Cache + L1D = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1D, + + /// for measuring Level 1 Instruction Cache + L1I = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_L1I, + + /// for measuring Last-Level Cache + LL = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_LL, + + /// for measuring the Data TLB + DTLB = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_DTLB, + + /// for measuring the Instruction TLB + ITLB = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_ITLB, + + /// for measuring the branch prediction unit + BPU = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_BPU, + + /// (since Linux 3.1) for measuring local memory accesses + NODE = bindings::perf_hw_cache_id_PERF_COUNT_HW_CACHE_NODE, +} + +/// What sort of cache operation we would like to observe. +/// +/// This is used in the `Cache` type as part of the identification of a cache +/// event. Each variant here corresponds to a particular +/// `PERF_COUNT_HW_CACHE_OP_...` constant supported by the +/// [`perf_event_open`][man] system call. +/// +/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +#[repr(u32)] +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum CacheOp { + /// Read accesses. + READ = bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_READ, + + /// Write accesses. + WRITE = bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_WRITE, + + /// Prefetch accesses. + PREFETCH = bindings::perf_hw_cache_op_id_PERF_COUNT_HW_CACHE_OP_PREFETCH, +} + +#[repr(u32)] +/// What sort of cache result we're interested in observing. +/// +/// `ACCESS` counts the total number of operations performed on the cache, +/// whereas `MISS` counts only those requests that the cache could not satisfy. +/// Treating `MISS` as a fraction of `ACCESS` gives you the cache's miss rate. +/// +/// This is used used in the `Cache` type as part of the identification of a +/// cache event. Each variant here corresponds to a particular +/// `PERF_COUNT_HW_CACHE_RESULT_...` constant supported by the +/// [`perf_event_open`][man] system call. +/// +/// [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum CacheResult { + /// to measure accesses + ACCESS = bindings::perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_ACCESS, + + /// to measure misses + MISS = bindings::perf_hw_cache_op_result_id_PERF_COUNT_HW_CACHE_RESULT_MISS, +} diff --git a/vendor/perf-event/src/lib.rs b/vendor/perf-event/src/lib.rs new file mode 100644 index 000000000..abc66bfab --- /dev/null +++ b/vendor/perf-event/src/lib.rs @@ -0,0 +1,1043 @@ +//! A performance monitoring API for Linux. +//! +//! This crate provides access to processor and kernel counters for things like +//! instruction completions, cache references and misses, branch predictions, +//! context switches, page faults, and so on. +//! +//! For example, to compare the number of clock cycles elapsed with the number +//! of instructions completed during one call to `println!`: +//! +//! use perf_event::{Builder, Group}; +//! use perf_event::events::Hardware; +//! +//! fn main() -> std::io::Result<()> { +//! // A `Group` lets us enable and disable several counters atomically. +//! let mut group = Group::new()?; +//! let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; +//! let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; +//! +//! let vec = (0..=51).collect::<Vec<_>>(); +//! +//! group.enable()?; +//! println!("{:?}", vec); +//! group.disable()?; +//! +//! let counts = group.read()?; +//! println!("cycles / instructions: {} / {} ({:.2} cpi)", +//! counts[&cycles], +//! counts[&insns], +//! (counts[&cycles] as f64 / counts[&insns] as f64)); +//! +//! Ok(()) +//! } +//! +//! This crate is built on top of the Linux [`perf_event_open`][man] system +//! call; that documentation has the authoritative explanations of exactly what +//! all the counters mean. +//! +//! There are two main types for measurement: +//! +//! - A [`Counter`] is an individual counter. Use [`Builder`] to +//! construct one. +//! +//! - A [`Group`] is a collection of counters that can be enabled and +//! disabled atomically, so that they cover exactly the same period of +//! execution, allowing meaningful comparisons of the individual values. +//! +//! If you're familiar with the kernel API already: +//! +//! - A `Builder` holds the arguments to a `perf_event_open` call: +//! a `struct perf_event_attr` and a few other fields. +//! +//! - `Counter` and `Group` objects are just event file descriptors, together +//! with their kernel id numbers, and some other details you need to +//! actually use them. They're different types because they yield different +//! types of results, and because you can't retrieve a `Group`'s counts +//! without knowing how many members it has. +//! +//! ### Call for PRs +//! +//! Linux's `perf_event_open` API can report all sorts of things this crate +//! doesn't yet understand: stack traces, logs of executable and shared library +//! activity, tracepoints, kprobes, uprobes, and so on. And beyond the counters +//! in the kernel header files, there are others that can only be found at +//! runtime by consulting `sysfs`, specific to particular processors and +//! devices. For example, modern Intel processors have counters that measure +//! power consumption in Joules. +//! +//! If you find yourself in need of something this crate doesn't support, please +//! consider submitting a pull request. +//! +//! [man]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html + +#![deny(missing_docs)] + +use events::Event; +use libc::pid_t; +use perf_event_open_sys as sys; +use perf_event_open_sys::bindings::perf_event_attr; +use std::fs::File; +use std::io::{self, Read}; +use std::os::raw::{c_int, c_uint, c_ulong}; +use std::os::unix::io::{AsRawFd, FromRawFd}; + +pub mod events; + +/// A counter for one kind of kernel or hardware event. +/// +/// A `Counter` represents a single performance monitoring counter. You select +/// what sort of event you'd like to count when the `Counter` is created, then +/// you can enable and disable the counter, call its [`read`] method to +/// retrieve the current count, and reset it to zero. +/// +/// A `Counter`'s value is always a `u64`. +/// +/// For example, this counts the number of instructions retired (completed) +/// during a call to `println!`. +/// +/// use perf_event::Builder; +/// +/// fn main() -> std::io::Result<()> { +/// let mut counter = Builder::new().build()?; +/// +/// let vec = (0..=51).collect::<Vec<_>>(); +/// +/// counter.enable()?; +/// println!("{:?}", vec); +/// counter.disable()?; +/// +/// println!("{} instructions retired", counter.read()?); +/// +/// Ok(()) +/// } +/// +/// It is often useful to count several different quantities over the same +/// period of time. For example, if you want to measure the average number of +/// clock cycles used per instruction, you must count both clock cycles and +/// instructions retired, for the same range of execution. The [`Group`] type +/// lets you enable, disable, read, and reset any number of counters +/// simultaneously. +/// +/// When a counter is dropped, its kernel resources are freed along with it. +/// +/// Internally, a `Counter` is just a wrapper around an event file descriptor. +/// +/// [`read`]: Counter::read +pub struct Counter { + /// The file descriptor for this counter, returned by `perf_event_open`. + /// + /// When a `Counter` is dropped, this `File` is dropped, and the kernel + /// removes the counter from any group it belongs to. + file: File, + + /// The unique id assigned to this counter by the kernel. + id: u64, +} + +/// A builder for [`Counter`]s. +/// +/// There are dozens of parameters that influence a `Counter`'s behavior. +/// `Builder` lets you construct a `Counter` by specifying only those parameters +/// for which you don't want the default value. +/// +/// A freshly built `Counter` is disabled. To begin counting events, you must +/// call [`enable`] on the `Counter` or the `Group` to which it belongs. +/// +/// Internally, a `Builder` is just a wrapper around the kernel's +/// `struct perf_event_attr` type. See the [perf_event_open(2)] man page for details. +/// +/// For example, if you want a `Counter` for instructions retired by the current +/// process, those are `Builder`'s defaults, so you need only write: +/// +/// # use perf_event::Builder; +/// # fn main() -> std::io::Result<()> { +/// let mut insns = Builder::new().build()?; +/// # Ok(()) } +/// +/// The [`kind`] method lets you specify what sort of event you want to +/// count. So if you'd rather count branch instructions: +/// +/// # use perf_event::Builder; +/// # use perf_event::events::Hardware; +/// # fn main() -> std::io::Result<()> { +/// let mut insns = Builder::new() +/// .kind(Hardware::BRANCH_INSTRUCTIONS) +/// .build()?; +/// # Ok(()) } +/// +/// The [`group`] method lets you gather individual counters into `Group` +/// that can be enabled or disabled atomically: +/// +/// # use perf_event::{Builder, Group}; +/// # use perf_event::events::Hardware; +/// # fn main() -> std::io::Result<()> { +/// let mut group = Group::new()?; +/// let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; +/// let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; +/// # Ok(()) } +/// +/// Other methods let you select: +/// +/// - specific processes or cgroups to observe +/// - specific CPU cores to observe +/// +/// `Builder` supports only a fraction of the many knobs and dials Linux offers, +/// but hopefully it will acquire methods to support more of them as time goes +/// on. +/// +/// [`enable`]: Counter::enable +/// [`kind`]: Builder::kind +/// [`group`]: Builder::group +/// [perf_event_open(2)]: http://man7.org/linux/man-pages/man2/perf_event_open.2.html +pub struct Builder<'a> { + attrs: perf_event_attr, + who: EventPid<'a>, + cpu: Option<usize>, + group: Option<&'a mut Group>, +} + +#[derive(Debug)] +enum EventPid<'a> { + /// Monitor the calling process. + ThisProcess, + + /// Monitor the given pid. + Other(pid_t), + + /// Monitor members of the given cgroup. + CGroup(&'a File), +} + +/// A group of counters that can be managed as a unit. +/// +/// A `Group` represents a group of [`Counter`]s that can be enabled, +/// disabled, reset, or read as a single atomic operation. This is necessary if +/// you want to compare counter values, produce ratios, and so on, since those +/// operations are only meaningful on counters that cover exactly the same +/// period of execution. +/// +/// A `Counter` is placed in a group when it is created, by calling the +/// `Builder`'s [`group`] method. A `Group`'s [`read`] method returns values +/// of all its member counters at once as a [`Counts`] value, which can be +/// indexed by `Counter` to retrieve a specific value. +/// +/// For example, the following program computes the average number of cycles +/// used per instruction retired for a call to `println!`: +/// +/// # fn main() -> std::io::Result<()> { +/// use perf_event::{Builder, Group}; +/// use perf_event::events::Hardware; +/// +/// let mut group = Group::new()?; +/// let cycles = Builder::new().group(&mut group).kind(Hardware::CPU_CYCLES).build()?; +/// let insns = Builder::new().group(&mut group).kind(Hardware::INSTRUCTIONS).build()?; +/// +/// let vec = (0..=51).collect::<Vec<_>>(); +/// +/// group.enable()?; +/// println!("{:?}", vec); +/// group.disable()?; +/// +/// let counts = group.read()?; +/// println!("cycles / instructions: {} / {} ({:.2} cpi)", +/// counts[&cycles], +/// counts[&insns], +/// (counts[&cycles] as f64 / counts[&insns] as f64)); +/// # Ok(()) } +/// +/// The lifetimes of `Counter`s and `Group`s are independent: placing a +/// `Counter` in a `Group` does not take ownership of the `Counter`, nor must +/// the `Counter`s in a group outlive the `Group`. If a `Counter` is dropped, it +/// is simply removed from its `Group`, and omitted from future results. If a +/// `Group` is dropped, its individual counters continue to count. +/// +/// Enabling or disabling a `Group` affects each `Counter` that belongs to it. +/// Subsequent reads from the `Counter` will not reflect activity while the +/// `Group` was disabled, unless the `Counter` is re-enabled individually. +/// +/// A `Group` and its members must all observe the same tasks and cpus; mixing +/// these makes building the `Counter` return an error. Unfortunately, there is +/// no way at present to specify a `Group`'s task and cpu, so you can only use +/// `Group` on the calling task. If this is a problem, please file an issue. +/// +/// Internally, a `Group` is just a wrapper around an event file descriptor. +/// +/// ## Limits on group size +/// +/// Hardware counters are implemented using special-purpose registers on the +/// processor, of which there are only a fixed number. (For example, an Intel +/// high-end laptop processor from 2015 has four such registers per virtual +/// processor.) Without using groups, if you request more hardware counters than +/// the processor can actually support, a complete count isn't possible, but the +/// kernel will rotate the processor's real registers amongst the measurements +/// you've requested to at least produce a sample. +/// +/// But since the point of a counter group is that its members all cover exactly +/// the same period of time, this tactic can't be applied to support large +/// groups. If the kernel cannot schedule a group, its counters remain zero. I +/// think you can detect this situation by comparing the group's [`time_enabled`] +/// and [`time_running`] values. It might also be useful to set the `pinned` bit, +/// which puts the counter in an error state if it's not able to be put on the +/// CPU; see [#10]. +/// +/// According to the `perf_list(1)` man page, you may be able to free up a +/// hardware counter by disabling the kernel's NMI watchdog, which reserves one +/// for detecting kernel hangs: +/// +/// ```ignore +/// $ echo 0 > /proc/sys/kernel/nmi_watchdog +/// ``` +/// +/// You can reenable the watchdog when you're done like this: +/// +/// ```ignore +/// $ echo 1 > /proc/sys/kernel/nmi_watchdog +/// ``` +/// +/// [`group`]: Builder::group +/// [`read`]: Group::read +/// [`#5`]: https://github.com/jimblandy/perf-event/issues/5 +/// [`#10`]: https://github.com/jimblandy/perf-event/issues/10 +/// [`time_enabled`]: Counts::time_enabled +/// [`time_running`]: Counts::time_running +pub struct Group { + /// The file descriptor for this counter, returned by `perf_event_open`. + /// This counter itself is for the dummy software event, so it's not + /// interesting. + file: File, + + /// The unique id assigned to this group by the kernel. We only use this for + /// assertions. + id: u64, + + /// An upper bound on the number of Counters in this group. This lets us + /// allocate buffers of sufficient size for for PERF_FORMAT_GROUP reads. + /// + /// There's no way to ask the kernel how many members a group has. And if we + /// pass a group read a buffer that's too small, the kernel won't just + /// return a truncated result; it returns ENOSPC and leaves the buffer + /// untouched. So the buffer just has to be large enough. + /// + /// Since we're borrowed while building group members, adding members can + /// increment this counter. But it's harder to decrement it when a member + /// gets dropped: we don't require that a Group outlive its members, so they + /// can't necessarily update their `Group`'s count from a `Drop` impl. So we + /// just increment, giving us an overestimate, and then correct the count + /// when we actually do a read. + /// + /// This includes the dummy counter for the group itself. + max_members: usize +} + +/// A collection of counts from a [`Group`] of counters. +/// +/// This is the type returned by calling [`read`] on a [`Group`]. +/// You can index it with a reference to a specific `Counter`: +/// +/// # fn main() -> std::io::Result<()> { +/// # use perf_event::{Builder, Group}; +/// # let mut group = Group::new()?; +/// # let cycles = Builder::new().group(&mut group).build()?; +/// # let insns = Builder::new().group(&mut group).build()?; +/// let counts = group.read()?; +/// println!("cycles / instructions: {} / {} ({:.2} cpi)", +/// counts[&cycles], +/// counts[&insns], +/// (counts[&cycles] as f64 / counts[&insns] as f64)); +/// # Ok(()) } +/// +/// Or you can iterate over the results it contains: +/// +/// # fn main() -> std::io::Result<()> { +/// # use perf_event::Group; +/// # let counts = Group::new()?.read()?; +/// for (id, value) in &counts { +/// println!("Counter id {} has value {}", id, value); +/// } +/// # Ok(()) } +/// +/// The `id` values produced by this iteration are internal identifiers assigned +/// by the kernel. You can use the [`Counter::id`] method to find a +/// specific counter's id. +/// +/// For some kinds of events, the kernel may use timesharing to give all +/// counters access to scarce hardware registers. You can see how long a group +/// was actually running versus the entire time it was enabled using the +/// `time_enabled` and `time_running` methods: +/// +/// # fn main() -> std::io::Result<()> { +/// # use perf_event::{Builder, Group}; +/// # let mut group = Group::new()?; +/// # let insns = Builder::new().group(&mut group).build()?; +/// # let counts = group.read()?; +/// let scale = counts.time_enabled() as f64 / +/// counts.time_running() as f64; +/// for (id, value) in &counts { +/// print!("Counter id {} has value {}", +/// id, (*value as f64 * scale) as u64); +/// if scale > 1.0 { +/// print!(" (estimated)"); +/// } +/// println!(); +/// } +/// +/// # Ok(()) } +/// +/// [`read`]: Group::read +pub struct Counts { + // Raw results from the `read`. + data: Vec<u64> +} + +/// The value of a counter, along with timesharing data. +/// +/// Some counters are implemented in hardware, and the processor can run +/// only a fixed number of them at a time. If more counters are requested +/// than the hardware can support, the kernel timeshares them on the +/// hardware. +/// +/// This struct holds the value of a counter, together with the time it was +/// enabled, and the proportion of that for which it was actually running. +#[repr(C)] +pub struct CountAndTime { + /// The counter value. + /// + /// The meaning of this field depends on how the counter was configured when + /// it was built; see ['Builder']. + pub count: u64, + + /// How long this counter was enabled by the program, in nanoseconds. + pub time_enabled: u64, + + /// How long the kernel actually ran this counter, in nanoseconds. + /// + /// If `time_enabled == time_running`, then the counter ran for the entire + /// period it was enabled, without interruption. Otherwise, the counter + /// shared the underlying hardware with others, and you should prorate its + /// value accordingly. + pub time_running: u64, +} + +impl<'a> EventPid<'a> { + // Return the `pid` arg and the `flags` bits representing `self`. + fn as_args(&self) -> (pid_t, u32) { + match self { + EventPid::ThisProcess => (0, 0), + EventPid::Other(pid) => (*pid, 0), + EventPid::CGroup(file) => + (file.as_raw_fd(), sys::bindings::PERF_FLAG_PID_CGROUP), + } + } +} + +impl<'a> Default for Builder<'a> { + fn default() -> Builder<'a> { + + let mut attrs = perf_event_attr::default(); + + // Setting `size` accurately will not prevent the code from working + // on older kernels. The module comments for `perf_event_open_sys` + // explain why in far too much detail. + attrs.size = std::mem::size_of::<perf_event_attr>() as u32; + + attrs.set_disabled(1); + attrs.set_exclude_kernel(1); // don't count time in kernel + attrs.set_exclude_hv(1); // don't count time in hypervisor + + // Request data for `time_enabled` and `time_running`. + attrs.read_format |= sys::bindings::perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED as u64 | + sys::bindings::perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING as u64; + + let kind = Event::Hardware(events::Hardware::INSTRUCTIONS); + attrs.type_ = kind.as_type(); + attrs.config = kind.as_config(); + + Builder { + attrs, + who: EventPid::ThisProcess, + cpu: None, + group: None, + } + } +} + +impl<'a> Builder<'a> { + /// Return a new `Builder`, with all parameters set to their defaults. + pub fn new() -> Builder<'a> { + Builder::default() + } + + /// Observe the calling process. (This is the default.) + pub fn observe_self(mut self) -> Builder<'a> { + self.who = EventPid::ThisProcess; + self + } + + /// Observe the process with the given process id. This requires + /// [`CAP_SYS_PTRACE`][man-capabilities] capabilities. + /// + /// [man-capabilities]: http://man7.org/linux/man-pages/man7/capabilities.7.html + pub fn observe_pid(mut self, pid: pid_t) -> Builder<'a> { + self.who = EventPid::Other(pid); + self + } + + /// Observe code running in the given [cgroup][man-cgroups] (container). The + /// `cgroup` argument should be a `File` referring to the cgroup's directory + /// in the cgroupfs filesystem. + /// + /// [man-cgroups]: http://man7.org/linux/man-pages/man7/cgroups.7.html + pub fn observe_cgroup(mut self, cgroup: &'a File) -> Builder<'a> { + self.who = EventPid::CGroup(cgroup); + self + } + + /// Observe only code running on the given CPU core. + pub fn one_cpu(mut self, cpu: usize) -> Builder<'a> { + self.cpu = Some(cpu); + self + } + + /// Observe code running on any CPU core. (This is the default.) + pub fn any_cpu(mut self) -> Builder<'a> { + self.cpu = None; + self + } + + /// Set whether this counter is inherited by new threads. + /// + /// When this flag is set, this counter observes activity in new threads + /// created by any thread already being observed. + /// + /// By default, the flag is unset: counters are not inherited, and observe + /// only the threads specified when they are created. + /// + /// This flag cannot be set if the counter belongs to a `Group`. Doing so + /// will result in an error when the counter is built. This is a kernel + /// limitation. + pub fn inherit(mut self, inherit: bool) -> Builder<'a> { + let flag = if inherit { 1 } else { 0 }; + self.attrs.set_inherit(flag); + self + } + + /// Count events of the given kind. This accepts an [`Event`] value, + /// or any type that can be converted to one, so you can pass [`Hardware`], + /// [`Software`] and [`Cache`] values directly. + /// + /// The default is to count retired instructions, or + /// `Hardware::INSTRUCTIONS` events. + /// + /// For example, to count level 1 data cache references and misses, pass the + /// appropriate `events::Cache` values: + /// + /// # fn main() -> std::io::Result<()> { + /// use perf_event::{Builder, Group}; + /// use perf_event::events::{Cache, CacheOp, CacheResult, WhichCache}; + /// + /// const ACCESS: Cache = Cache { + /// which: WhichCache::L1D, + /// operation: CacheOp::READ, + /// result: CacheResult::ACCESS, + /// }; + /// const MISS: Cache = Cache { result: CacheResult::MISS, ..ACCESS }; + /// + /// let mut group = Group::new()?; + /// let access_counter = Builder::new().group(&mut group).kind(ACCESS).build()?; + /// let miss_counter = Builder::new().group(&mut group).kind(MISS).build()?; + /// # Ok(()) } + /// + /// [`Hardware`]: events::Hardware + /// [`Software`]: events::Software + /// [`Cache`]: events::Cache + pub fn kind<K: Into<Event>>(mut self, kind: K) -> Builder<'a> { + let kind = kind.into(); + self.attrs.type_ = kind.as_type(); + self.attrs.config = kind.as_config(); + self + } + + /// Place the counter in the given [`Group`]. Groups allow a set of counters + /// to be enabled, disabled, or read as a single atomic operation, so that + /// the counts can be usefully compared. + /// + /// [`Group`]: struct.Group.html + pub fn group(mut self, group: &'a mut Group) -> Builder<'a> { + self.group = Some(group); + + // man page: "Members of a group are usually initialized with disabled + // set to zero." + self.attrs.set_disabled(0); + + self + } + + /// Construct a [`Counter`] according to the specifications made on this + /// `Builder`. + /// + /// A freshly built `Counter` is disabled. To begin counting events, you + /// must call [`enable`] on the `Counter` or the `Group` to which it belongs. + /// + /// If the `Builder` requests features that the running kernel does not + /// support, it returns `Err(e)` where `e.kind() == ErrorKind::Other` and + /// `e.raw_os_error() == Some(libc::E2BIG)`. + /// + /// Unfortunately, problems in counter configuration are detected at this + /// point, by the kernel, not earlier when the offending request is made on + /// the `Builder`. The kernel's returned errors are not always helpful. + /// + /// [`Counter`]: struct.Counter.html + /// [`enable`]: struct.Counter.html#method.enable + pub fn build(mut self) -> std::io::Result<Counter> { + let cpu = match self.cpu { + Some(cpu) => cpu as c_int, + None => -1, + }; + let (pid, flags) = self.who.as_args(); + let group_fd = match self.group { + Some(ref mut g) => { + g.max_members += 1; + g.file.as_raw_fd() as c_int + } + None => -1, + }; + + let file = unsafe { + File::from_raw_fd(check_raw_syscall(|| { + sys::perf_event_open(&mut self.attrs, pid, cpu, group_fd, flags as c_ulong) + })?) + }; + + // If we're going to be part of a Group, retrieve the ID the kernel + // assigned us, so we can find our results in a Counts structure. Even + // if we're not part of a group, we'll use it in `Debug` output. + let mut id = 0_64; + check_errno_syscall(|| unsafe { + sys::ioctls::ID(file.as_raw_fd(), &mut id) + })?; + + Ok(Counter { file, id, }) + } +} + +impl Counter { + /// Return this counter's kernel-assigned unique id. + /// + /// This can be useful when iterating over [`Counts`]. + /// + /// [`Counts`]: struct.Counts.html + pub fn id(&self) -> u64 { + self.id + } + + /// Allow this `Counter` to begin counting its designated event. + /// + /// This does not affect whatever value the `Counter` had previously; new + /// events add to the current count. To clear a `Counter`, use the + /// [`reset`] method. + /// + /// Note that `Group` also has an [`enable`] method, which enables all + /// its member `Counter`s as a single atomic operation. + /// + /// [`reset`]: #method.reset + /// [`enable`]: struct.Group.html#method.enable + pub fn enable(&mut self) -> io::Result<()> { + check_errno_syscall(|| unsafe { + sys::ioctls::ENABLE(self.file.as_raw_fd(), 0) + }).map(|_| ()) + } + + /// Make this `Counter` stop counting its designated event. Its count is + /// unaffected. + /// + /// Note that `Group` also has a [`disable`] method, which disables all + /// its member `Counter`s as a single atomic operation. + /// + /// [`disable`]: struct.Group.html#method.disable + pub fn disable(&mut self) -> io::Result<()> { + check_errno_syscall(|| unsafe { + sys::ioctls::DISABLE(self.file.as_raw_fd(), 0) + }).map(|_| ()) + } + + /// Reset the value of this `Counter` to zero. + /// + /// Note that `Group` also has a [`reset`] method, which resets all + /// its member `Counter`s as a single atomic operation. + /// + /// [`reset`]: struct.Group.html#method.reset + pub fn reset(&mut self) -> io::Result<()> { + check_errno_syscall(|| unsafe { + sys::ioctls::RESET(self.file.as_raw_fd(), 0) + }).map(|_| ()) + } + + /// Return this `Counter`'s current value as a `u64`. + /// + /// Consider using the [`read_count_and_time`] method instead of this one. Some + /// counters are implemented in hardware, and the processor can support only + /// a certain number running at a time. If more counters are requested than + /// the hardware can support, the kernel timeshares them on the hardware. + /// This method gives you no indication whether this has happened; + /// `read_count_and_time` does. + /// + /// Note that `Group` also has a [`read`] method, which reads all + /// its member `Counter`s' values at once. + /// + /// [`read`]: Group::read + /// [`read_count_and_time`]: Counter::read_count_and_time + pub fn read(&mut self) -> io::Result<u64> { + Ok(self.read_count_and_time()?.count) + } + + /// Return this `Counter`'s current value and timesharing data. + /// + /// Some counters are implemented in hardware, and the processor can run + /// only a fixed number of them at a time. If more counters are requested + /// than the hardware can support, the kernel timeshares them on the + /// hardware. + /// + /// This method returns a [`CountAndTime`] struct, whose `count` field holds + /// the counter's value, and whose `time_enabled` and `time_running` fields + /// indicate how long you had enabled the counter, and how long the counter + /// was actually scheduled on the processor. This lets you detect whether + /// the counter was timeshared, and adjust your use accordingly. Times + /// are reported in nanoseconds. + /// + /// # use perf_event::Builder; + /// # fn main() -> std::io::Result<()> { + /// # let mut counter = Builder::new().build()?; + /// let cat = counter.read_count_and_time()?; + /// if cat.time_running == 0 { + /// println!("No data collected."); + /// } else if cat.time_running < cat.time_enabled { + /// // Note: this way of scaling is accurate, but `u128` division + /// // is usually implemented in software, which may be slow. + /// println!("{} instructions (estimated)", + /// (cat.count as u128 * + /// cat.time_enabled as u128 / cat.time_running as u128) as u64); + /// } else { + /// println!("{} instructions", cat.count); + /// } + /// # Ok(()) } + /// + /// Note that `Group` also has a [`read`] method, which reads all + /// its member `Counter`s' values at once. + /// + /// [`read`]: Group::read + pub fn read_count_and_time(&mut self) -> io::Result<CountAndTime> { + let mut buf = [0_u64; 3]; + self.file.read_exact(u64::slice_as_bytes_mut(&mut buf))?; + + let cat = CountAndTime { + count: buf[0], + time_enabled: buf[1], + time_running: buf[2], + }; + + // Does the kernel ever return nonsense? + assert!(cat.time_running <= cat.time_enabled); + + Ok(cat) + } +} + +impl std::fmt::Debug for Counter { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Counter {{ fd: {}, id: {} }}", + self.file.as_raw_fd(), self.id) + } +} + +impl Group { + /// Construct a new, empty `Group`. + #[allow(unused_parens)] + pub fn new() -> io::Result<Group> { + // Open a placeholder perf counter that we can add other events to. + let mut attrs = perf_event_attr::default(); + attrs.size = std::mem::size_of::<perf_event_attr>() as u32; + attrs.type_ = sys::bindings::perf_type_id_PERF_TYPE_SOFTWARE; + attrs.config = sys::bindings::perf_sw_ids_PERF_COUNT_SW_DUMMY as u64; + attrs.set_disabled(1); + attrs.set_exclude_kernel(1); + attrs.set_exclude_hv(1); + + // Arrange to be able to identify the counters we read back. + attrs.read_format = (sys::bindings::perf_event_read_format_PERF_FORMAT_TOTAL_TIME_ENABLED | + sys::bindings::perf_event_read_format_PERF_FORMAT_TOTAL_TIME_RUNNING | + sys::bindings::perf_event_read_format_PERF_FORMAT_ID | + sys::bindings::perf_event_read_format_PERF_FORMAT_GROUP) as u64; + + let file = unsafe { + File::from_raw_fd(check_raw_syscall(|| { + sys::perf_event_open(&mut attrs, 0, -1, -1, 0) + })?) + }; + + // Retrieve the ID the kernel assigned us. + let mut id = 0_64; + check_errno_syscall(|| unsafe { + sys::ioctls::ID(file.as_raw_fd(), &mut id) + })?; + + Ok(Group { file, id, max_members: 1 }) + } + + /// Allow all `Counter`s in this `Group` to begin counting their designated + /// events, as a single atomic operation. + /// + /// This does not affect whatever values the `Counter`s had previously; new + /// events add to the current counts. To clear the `Counter`s, use the + /// [`reset`] method. + /// + /// [`reset`]: #method.reset + pub fn enable(&mut self) -> io::Result<()> { + self.generic_ioctl(sys::ioctls::ENABLE) + } + + /// Make all `Counter`s in this `Group` stop counting their designated + /// events, as a single atomic operation. Their counts are unaffected. + pub fn disable(&mut self) -> io::Result<()> { + self.generic_ioctl(sys::ioctls::DISABLE) + } + + /// Reset all `Counter`s in this `Group` to zero, as a single atomic operation. + pub fn reset(&mut self) -> io::Result<()> { + self.generic_ioctl(sys::ioctls::RESET) + } + + /// Perform some group ioctl. + /// + /// `f` must be a syscall that sets `errno` and returns `-1` on failure. + fn generic_ioctl(&mut self, f: unsafe fn(c_int, c_uint) -> c_int) -> io::Result<()> { + check_errno_syscall(|| unsafe { + f(self.file.as_raw_fd(), + sys::bindings::perf_event_ioc_flags_PERF_IOC_FLAG_GROUP) + }).map(|_| ()) + } + + /// Return the values of all the `Counter`s in this `Group` as a [`Counts`] + /// value. + /// + /// A `Counts` value is a map from specific `Counter`s to their values. You + /// can find a specific `Counter`'s value by indexing: + /// + /// ```ignore + /// let mut group = Group::new()?; + /// let counter1 = Builder::new().group(&mut group).kind(...).build()?; + /// let counter2 = Builder::new().group(&mut group).kind(...).build()?; + /// ... + /// let counts = group.read()?; + /// println!("Rhombus inclinations per taxi medallion: {} / {} ({:.0}%)", + /// counts[&counter1], + /// counts[&counter2], + /// (counts[&counter1] as f64 / counts[&counter2] as f64) * 100.0); + /// ``` + /// + /// [`Counts`]: struct.Counts.html + pub fn read(&mut self) -> io::Result<Counts> { + // Since we passed `PERF_FORMAT_{ID,GROUP,TOTAL_TIME_{ENABLED,RUNNING}}`, + // the data we'll read has the form: + // + // struct read_format { + // u64 nr; /* The number of events */ + // u64 time_enabled; /* if PERF_FORMAT_TOTAL_TIME_ENABLED */ + // u64 time_running; /* if PERF_FORMAT_TOTAL_TIME_RUNNING */ + // struct { + // u64 value; /* The value of the event */ + // u64 id; /* if PERF_FORMAT_ID */ + // } values[nr]; + // }; + let mut data = vec![0_u64; 3 + 2 * self.max_members]; + self.file.read(u64::slice_as_bytes_mut(&mut data))?; + + let counts = Counts { data }; + + // CountsIter assumes that the group's dummy count appears first. + assert_eq!(counts.nth_ref(0).0, self.id); + + // Does the kernel ever return nonsense? + assert!(counts.time_running() <= counts.time_enabled()); + + // Update `max_members` for the next read. + self.max_members = counts.len(); + + Ok(counts) + } +} + +impl std::fmt::Debug for Group { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(fmt, "Group {{ fd: {}, id: {} }}", + self.file.as_raw_fd(), self.id) + } +} + +impl Counts { + /// Return the number of counters this `Counts` holds results for. + pub fn len(&self) -> usize { + self.data[0] as usize + } + + /// Return the number of nanoseconds the `Group` was enabled that + /// contributed to this `Counts`' contents. + pub fn time_enabled(&self) -> u64 { + self.data[1] + } + + /// Return the number of nanoseconds the `Group` was actually collecting + /// counts that contributed to this `Counts`' contents. + pub fn time_running(&self) -> u64 { + self.data[2] + } + + /// Return a range of indexes covering the count and id of the `n`'th counter. + fn nth_index(n: usize) -> std::ops::Range<usize> { + let base = 3 + 2 * n; + base .. base + 2 + } + + /// Return the id and count of the `n`'th counter. This returns a reference + /// to the count, for use by the `Index` implementation. + fn nth_ref(&self, n: usize) -> (u64, &u64) { + let id_val = &self.data[Counts::nth_index(n)]; + + // (id, &value) + (id_val[1], &id_val[0]) + } +} + +/// An iterator over the counter values in a [`Counts`], returned by +/// [`Group::read`]. +/// +/// Each item is a pair `(id, &value)`, where `id` is the number assigned to the +/// counter by the kernel (see `Counter::id`), and `value` is that counter's +/// value. +/// +/// [`Counts`]: struct.Counts.html +/// [`Counter::id`]: struct.Counter.html#method.id +/// [`Group::read`]: struct.Group.html#method.read +pub struct CountsIter<'c> { + counts: &'c Counts, + next: usize +} + +impl<'c> Iterator for CountsIter<'c> { + type Item = (u64, &'c u64); + fn next(&mut self) -> Option<(u64, &'c u64)> { + if self.next >= self.counts.len() { + return None; + } + let result = self.counts.nth_ref(self.next); + self.next += 1; + return Some(result); + } +} + +impl<'c> IntoIterator for &'c Counts { + type Item = (u64, &'c u64); + type IntoIter = CountsIter<'c>; + fn into_iter(self) -> CountsIter<'c> { + CountsIter { + counts: self, + next: 1, // skip the `Group` itself, it's just a dummy. + } + } +} + +impl Counts { + /// Return the value recorded for `member` in `self`, or `None` if `member` + /// is not present. + /// + /// If you know that `member` is in the group, you can simply index: + /// + /// # fn main() -> std::io::Result<()> { + /// # use perf_event::{Builder, Group}; + /// # let mut group = Group::new()?; + /// # let cycle_counter = Builder::new().group(&mut group).build()?; + /// # let counts = group.read()?; + /// let cycles = counts[&cycle_counter]; + /// # Ok(()) } + pub fn get(&self, member: &Counter) -> Option<&u64> { + self.into_iter() + .find(|&(id, _)| id == member.id) + .map(|(_, value)| value) + } + + /// Return an iterator over the counts in `self`. + /// + /// # fn main() -> std::io::Result<()> { + /// # use perf_event::Group; + /// # let counts = Group::new()?.read()?; + /// for (id, value) in &counts { + /// println!("Counter id {} has value {}", id, value); + /// } + /// # Ok(()) } + /// + /// Each item is a pair `(id, &value)`, where `id` is the number assigned to + /// the counter by the kernel (see `Counter::id`), and `value` is that + /// counter's value. + pub fn iter(&self) -> CountsIter { + <&Counts as IntoIterator>::into_iter(self) + } +} + +impl std::ops::Index<&Counter> for Counts { + type Output = u64; + fn index(&self, index: &Counter) -> &u64 { + self.get(index).unwrap() + } +} + +impl std::fmt::Debug for Counts { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.debug_map().entries(self.into_iter()).finish() + } +} + +unsafe trait SliceAsBytesMut: Sized { + fn slice_as_bytes_mut(slice: &mut [Self]) -> &mut [u8] { + unsafe { + std::slice::from_raw_parts_mut(slice.as_mut_ptr() as *mut u8, + std::mem::size_of_val(slice)) + } + } +} + +unsafe impl SliceAsBytesMut for u64 { } + +/// Produce an `io::Result` from a raw system call. +/// +/// A 'raw' system call is one that reports failure by returning negated raw OS +/// error value. +fn check_raw_syscall<F>(f: F) -> io::Result<c_int> +where F: FnOnce() -> c_int +{ + let result = f(); + if result < 0 { + Err(io::Error::from_raw_os_error(-result)) + } else { + Ok(result) + } +} + +/// Produce an `io::Result` from an errno-style system call. +/// +/// An 'errno-style' system call is one that reports failure by returning -1 and +/// setting the C `errno` value when an error occurs. +fn check_errno_syscall<F, R>(f: F) -> io::Result<R> +where F: FnOnce() -> R, + R: PartialOrd + Default +{ + let result = f(); + if result < R::default() { + Err(io::Error::last_os_error()) + } else { + Ok(result) + } +} + +#[test] +fn simple_build() { + Builder::new().build().expect("Couldn't build default Counter"); +} diff --git a/vendor/perf-event/wrapper.h b/vendor/perf-event/wrapper.h new file mode 100644 index 000000000..b60268317 --- /dev/null +++ b/vendor/perf-event/wrapper.h @@ -0,0 +1,20 @@ +// This file is consumed by bindgen, called from our build.rs file. + +#include <linux/perf_event.h> +#include <asm/unistd.h> + +// bindgen won't capture +enum perf_event_ioctls { + ENABLE = PERF_EVENT_IOC_ENABLE, + DISABLE = PERF_EVENT_IOC_DISABLE, + REFRESH = PERF_EVENT_IOC_REFRESH, + RESET = PERF_EVENT_IOC_RESET, + PERIOD = PERF_EVENT_IOC_PERIOD, + SET_OUTPUT = PERF_EVENT_IOC_SET_OUTPUT, + SET_FILTER = PERF_EVENT_IOC_SET_FILTER, + ID = PERF_EVENT_IOC_ID, + SET_BPF = PERF_EVENT_IOC_SET_BPF, + PAUSE_OUTPUT = PERF_EVENT_IOC_PAUSE_OUTPUT, + QUERY_BPF = PERF_EVENT_IOC_QUERY_BPF, + MODIFY_ATTRIBUTES = PERF_EVENT_IOC_MODIFY_ATTRIBUTES, +}; |