From e7909d01f626f580fdd2fdeec248fd2a0c74a8a8 Mon Sep 17 00:00:00 2001 From: Gordon Marler Date: Mon, 16 Sep 2024 15:10:52 -0400 Subject: [PATCH 1/7] add initial --exclude_self option --- src/main.rs | 7 +++++++ src/profiler.rs | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/main.rs b/src/main.rs index e44e06e..780cc62 100644 --- a/src/main.rs +++ b/src/main.rs @@ -220,6 +220,12 @@ struct Cli { help = "Derived from constant MAX_PROCESSES" )] mapsize_rate_limits: u32, + // Exclude myself from profiling + #[arg( + long, + help = "Do not profile the profiler (myself)" + )] + exclude_self: bool, } /// Exit the main thread if any thread panics. We prefer this behaviour because pretty much every @@ -308,6 +314,7 @@ fn main() -> Result<(), Box> { mapsize_unwind_info_chunks: args.mapsize_unwind_info_chunks, mapsize_unwind_tables: args.mapsize_unwind_tables, mapsize_rate_limits: args.mapsize_rate_limits, + exclude_self: args.exclude_self, }; let (stop_signal_sender, stop_signal_receive) = bounded(1); diff --git a/src/profiler.rs b/src/profiler.rs index ba58c10..e728b16 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -197,6 +197,8 @@ pub struct Profiler<'bpf> { // Size of each perf buffer, in bytes perf_buffer_bytes: usize, session_duration: Duration, + // Whether this process should be excluded from profiling + exclude_self: bool, } // Static config @@ -1216,6 +1218,10 @@ impl Profiler<'_> { } fn should_profile(&self, pid: i32) -> bool { + if self.exclude_self && (pid == self->pid) { + return false; + } + if self.filter_pids.is_empty() { return true; } From 93144a9c4c503dc8d86debafdafce6c6ecc743f6 Mon Sep 17 00:00:00 2001 From: Gordon Marler Date: Wed, 18 Sep 2024 11:43:46 -0400 Subject: [PATCH 2/7] Add basic code to allow --exclude-self to work --- src/profiler.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/profiler.rs b/src/profiler.rs index e728b16..4302d6a 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -197,7 +197,7 @@ pub struct Profiler<'bpf> { // Size of each perf buffer, in bytes perf_buffer_bytes: usize, session_duration: Duration, - // Whether this process should be excluded from profiling + // Whether the profiler (this process) should be excluded from profiling exclude_self: bool, } @@ -328,6 +328,7 @@ pub struct ProfilerConfig { pub mapsize_unwind_info_chunks: u32, pub mapsize_unwind_tables: u32, pub mapsize_rate_limits: u32, + pub exclude_self: bool, } // Note that we normally pass in the defaults from Clap, and we don't want @@ -348,6 +349,7 @@ impl Default for ProfilerConfig { mapsize_unwind_info_chunks: 5000, mapsize_unwind_tables: 65, mapsize_rate_limits: 5000, + exclude_self: false, } } } @@ -400,6 +402,7 @@ impl Profiler<'_> { .lightswitch_config .verbose_logging .write(profiler_config.bpf_logging); + let exclude_self = profiler_config.exclude_self; let bpf = open_skel.load().expect("load skel"); info!("native unwinder BPF program loaded"); let native_unwinder_maps = bpf.maps(); @@ -493,6 +496,7 @@ impl Profiler<'_> { sample_freq, perf_buffer_bytes, session_duration: Duration::from_secs(5), + exclude_self, } } @@ -1218,7 +1222,7 @@ impl Profiler<'_> { } fn should_profile(&self, pid: i32) -> bool { - if self.exclude_self && (pid == self->pid) { + if self.exclude_self && (pid == std::process::id()) { return false; } From 16037fa1cfe142dde22474131c76bee87292f009 Mon Sep 17 00:00:00 2001 From: Gordon Marler Date: Wed, 18 Sep 2024 12:01:23 -0400 Subject: [PATCH 3/7] Convert PID from u32 to i32 --- src/profiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/profiler.rs b/src/profiler.rs index 4302d6a..c0f5f82 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -1222,7 +1222,7 @@ impl Profiler<'_> { } fn should_profile(&self, pid: i32) -> bool { - if self.exclude_self && (pid == std::process::id()) { + if self.exclude_self && (pid == std::process::id().try_into().unwrap()) { return false; } From dd0a4ad912566857ac6d8bffa32d0e639ff860f5 Mon Sep 17 00:00:00 2001 From: gmarler Date: Wed, 18 Sep 2024 18:03:59 +0000 Subject: [PATCH 4/7] update cli test fixture --- src/main.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main.rs b/src/main.rs index 780cc62..89279b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -406,8 +406,7 @@ mod tests { cmd.assert().success(); let actual = String::from_utf8(cmd.unwrap().stdout).unwrap(); insta::assert_yaml_snapshot!(actual, @r###" - --- - "Usage: lightswitch [OPTIONS]\n\nOptions:\n --pids \n Specific PIDs to profile\n\n --tids \n Specific TIDs to profile (these can be outside the PIDs selected above)\n\n --show-unwind-info \n Show unwind info for given binary\n\n --show-info \n Show build ID for given binary\n\n -D, --duration \n How long this agent will run in seconds\n \n [default: 18446744073709551615]\n\n --libbpf-logs\n Enable libbpf logs. This includes the BPF verifier output\n\n --bpf-logging\n Enable BPF programs logging\n\n --logging \n Set lightswitch's logging level\n \n [default: info]\n [possible values: trace, debug, info, warn, error]\n\n --sample-freq \n Per-CPU Sampling Frequency in Hz\n \n [default: 19]\n\n --profile-format \n Output file for Flame Graph in SVG format\n \n [default: flame-graph]\n [possible values: none, flame-graph, pprof]\n\n --profile-name \n Name for the generated profile\n\n --sender \n Where to write the profile\n \n [default: local-disk]\n\n Possible values:\n - none: Discard the profile. Used for kernel tests\n - local-disk\n - remote\n\n --perf-buffer-bytes \n Size of each profiler perf buffer, in bytes (must be a power of 2)\n \n [default: 524288]\n\n --mapsize-info\n Print eBPF map sizes after creation\n\n --mapsize-stacks \n max number of individual stacks to capture before aggregation\n \n [default: 100000]\n\n --mapsize-aggregated-stacks \n Derived from constant MAX_AGGREGATED_STACKS_ENTRIES - max number of unique stacks after aggregation\n \n [default: 10000]\n\n --mapsize-unwind-info-chunks \n max number of chunks allowed inside a shard\n \n [default: 5000]\n\n --mapsize-unwind-tables \n Derived from constant MAX_UNWIND_INFO_SHARDS\n \n [default: 65]\n\n --mapsize-rate-limits \n Derived from constant MAX_PROCESSES\n \n [default: 5000]\n\n -h, --help\n Print help (see a summary with '-h')\n" + "Usage: lightswitch [OPTIONS]\n\nOptions:\n --pids \n Specific PIDs to profile\n\n --tids \n Specific TIDs to profile (these can be outside the PIDs selected above)\n\n --show-unwind-info \n Show unwind info for given binary\n\n --show-info \n Show build ID for given binary\n\n -D, --duration \n How long this agent will run in seconds\n \n [default: 18446744073709551615]\n\n --libbpf-logs\n Enable libbpf logs. This includes the BPF verifier output\n\n --bpf-logging\n Enable BPF programs logging\n\n --logging \n Set lightswitch's logging level\n \n [default: info]\n [possible values: trace, debug, info, warn, error]\n\n --sample-freq \n Per-CPU Sampling Frequency in Hz\n \n [default: 19]\n\n --profile-format \n Output file for Flame Graph in SVG format\n \n [default: flame-graph]\n [possible values: none, flame-graph, pprof]\n\n --profile-name \n Name for the generated profile\n\n --sender \n Where to write the profile\n \n [default: local-disk]\n\n Possible values:\n - none: Discard the profile. Used for kernel tests\n - local-disk\n - remote\n\n --perf-buffer-bytes \n Size of each profiler perf buffer, in bytes (must be a power of 2)\n \n [default: 524288]\n\n --mapsize-info\n Print eBPF map sizes after creation\n\n --mapsize-stacks \n max number of individual stacks to capture before aggregation\n \n [default: 100000]\n\n --mapsize-aggregated-stacks \n Derived from constant MAX_AGGREGATED_STACKS_ENTRIES - max number of unique stacks after aggregation\n \n [default: 10000]\n\n --mapsize-unwind-info-chunks \n max number of chunks allowed inside a shard\n \n [default: 5000]\n\n --mapsize-unwind-tables \n Derived from constant MAX_UNWIND_INFO_SHARDS\n \n [default: 65]\n\n --mapsize-rate-limits \n Derived from constant MAX_PROCESSES\n \n [default: 5000]\n\n --exclude-self\n Do not profile the profiler (myself)\n\n -h, --help\n Print help (see a summary with '-h')\n" "###); } From e7ba3d633bd36f2da42d69d3881051b80fd08366 Mon Sep 17 00:00:00 2001 From: Javier Honduvilla Coto Date: Wed, 18 Sep 2024 12:06:07 -0400 Subject: [PATCH 5/7] Update nix deps (#73) - `hardeningDisable` to remove the newly added '-fzero-call-used-regs=used-gpr' flag to clang - fixes fornew clippy lints --- flake.lock | 29 ++++++++++++----------------- flake.nix | 8 +++----- lightswitch-proto/src/profile.rs | 1 + src/util.rs | 2 ++ 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/flake.lock b/flake.lock index 7b4ef37..eca6256 100644 --- a/flake.lock +++ b/flake.lock @@ -1,17 +1,12 @@ { "nodes": { "crane": { - "inputs": { - "nixpkgs": [ - "nixpkgs" - ] - }, "locked": { - "lastModified": 1718730147, - "narHash": "sha256-QmD6B6FYpuoCqu6ZuPJH896ItNquDkn0ulQlOn4ykN8=", + "lastModified": 1725409566, + "narHash": "sha256-PrtLmqhM6UtJP7v7IGyzjBFhbG4eOAHT6LPYOFmYfbk=", "owner": "ipetkov", "repo": "crane", - "rev": "32c21c29b034d0a93fdb2379d6fabc40fc3d0e6c", + "rev": "7e4586bad4e3f8f97a9271def747cf58c4b68f3c", "type": "github" }, "original": { @@ -25,11 +20,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1710146030, - "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "lastModified": 1726560853, + "narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a", "type": "github" }, "original": { @@ -40,11 +35,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1718714799, - "narHash": "sha256-FUZpz9rg3gL8NVPKbqU8ei1VkPLsTIfAJ2fdAf5qjak=", + "lastModified": 1726463316, + "narHash": "sha256-gI9kkaH0ZjakJOKrdjaI/VbaMEo9qBbSUl93DnU7f4c=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c00d587b1a1afbf200b1d8f0b0e4ba9deb1c7f0e", + "rev": "99dc8785f6a0adac95f5e2ab05cc2e1bf666d172", "type": "github" }, "original": { @@ -69,11 +64,11 @@ ] }, "locked": { - "lastModified": 1718849885, - "narHash": "sha256-Qfc5HKpQvGhWXox0WJVzLqrAcFm3uy6xtWRvVmrkLYc=", + "lastModified": 1726626348, + "narHash": "sha256-sYV7e1B1yLcxo8/h+/hTwzZYmaju2oObNiy5iRI0C30=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "bc1a236757cd5f6622f73838e551fb2035afa44a", + "rev": "6fd52ad8bd88f39efb2c999cc971921c2fb9f3a2", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 63855f6..be6170e 100644 --- a/flake.nix +++ b/flake.nix @@ -9,10 +9,7 @@ }; }; - crane = { - url = "github:ipetkov/crane"; - inputs.nixpkgs.follows = "nixpkgs"; - }; + crane.url = "github:ipetkov/crane"; }; outputs = { self, nixpkgs, flake-utils, rust-overlay, crane }: flake-utils.lib.eachSystem [ "x86_64-linux" "aarch64-linux" ] @@ -49,6 +46,7 @@ doCheck = false; buildInputs = buildInputs; nativeBuildInputs = nativeBuildInputs; + hardeningDisable = [ "all" ]; LIBCLANG_PATH = with pkgs; lib.makeLibraryPath [ llvmPackages_16.libclang ]; LIBBPF_SYS_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [ zlib.static elfutils' ]; }; @@ -83,7 +81,7 @@ cargo-insta # ocamlPackages.magic-trace ]; - + hardeningDisable = [ "all" ]; LIBCLANG_PATH = lib.makeLibraryPath [ llvmPackages_16.libclang ]; LIBBPF_SYS_LIBRARY_PATH = lib.makeLibraryPath [ zlib.static elfutils' ]; RUST_GDB = "${gdb}/bin/gdb"; diff --git a/lightswitch-proto/src/profile.rs b/lightswitch-proto/src/profile.rs index 2f765c2..dc4a45b 100644 --- a/lightswitch-proto/src/profile.rs +++ b/lightswitch-proto/src/profile.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] +#[allow(clippy::all)] pub mod pprof { include!(concat!(env!("OUT_DIR"), "/perftools.profiles.rs")); } diff --git a/src/util.rs b/src/util.rs index 4419404..6777e68 100644 --- a/src/util.rs +++ b/src/util.rs @@ -7,6 +7,8 @@ pub struct AddressBlockRange { pub prefix_len: u32, } +/// Calculate addresses for longest prefix match. +/// /// For a given address range, calculate all the prefix ranges to ensure searching /// with Longest Prefix Match returns the precise value we want. This is typically /// used in networking to select the right subnet. From 0eae0e77f2295756f8dba21aa547a7670d44e75f Mon Sep 17 00:00:00 2001 From: Gordon Marler Date: Wed, 18 Sep 2024 14:33:54 -0400 Subject: [PATCH 6/7] Make cargo fmt happy --- src/main.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/main.rs b/src/main.rs index 89279b8..cf0f7b3 100644 --- a/src/main.rs +++ b/src/main.rs @@ -221,10 +221,7 @@ struct Cli { )] mapsize_rate_limits: u32, // Exclude myself from profiling - #[arg( - long, - help = "Do not profile the profiler (myself)" - )] + #[arg(long, help = "Do not profile the profiler (myself)")] exclude_self: bool, } From 7b2864a0c5af46ed7ba05f7d7c18759430e30c9f Mon Sep 17 00:00:00 2001 From: Gordon Marler Date: Wed, 18 Sep 2024 16:09:21 -0400 Subject: [PATCH 7/7] cast better --- src/profiler.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/profiler.rs b/src/profiler.rs index c0f5f82..ef90608 100644 --- a/src/profiler.rs +++ b/src/profiler.rs @@ -1222,7 +1222,7 @@ impl Profiler<'_> { } fn should_profile(&self, pid: i32) -> bool { - if self.exclude_self && (pid == std::process::id().try_into().unwrap()) { + if self.exclude_self && pid == std::process::id() as i32 { return false; }