diff --git a/Cargo.lock b/Cargo.lock index e1e3c49dc..cb6864d79 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -45,6 +45,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" dependencies = [ "cfg-if", + "getrandom", "once_cell", "version_check", ] @@ -91,6 +92,12 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" +[[package]] +name = "arrayvec" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da52d66c7071e2e3fa2a1e5c6d088fec47b593032b254f5e980de8ea54454d6" + [[package]] name = "async-recursion" version = "1.0.0" @@ -231,6 +238,12 @@ version = "3.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba" +[[package]] +name = "bytemuck" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f" + [[package]] name = "byteorder" version = "1.4.3" @@ -460,9 +473,14 @@ version = "3.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71655c45cb9845d3270c9d6df84ebe72b4dad3c2ba3f7023ad47c144e4e473a5" dependencies = [ + "atty", "bitflags", + "clap_derive 3.2.18", "clap_lex 0.2.4", "indexmap", + "once_cell", + "strsim", + "termcolor", "textwrap", ] @@ -473,7 +491,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d63b9e9c07271b9957ad22c173bae2a4d9a81127680962039296abcd2f8251d" dependencies = [ "bitflags", - "clap_derive", + "clap_derive 4.0.21", "clap_lex 0.3.0", "is-terminal", "once_cell", @@ -481,6 +499,19 @@ dependencies = [ "termcolor", ] +[[package]] +name = "clap_derive" +version = "3.2.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea0c8bce528c4be4da13ea6fead8965e95b6073585a2f05204bd8f4119f82a65" +dependencies = [ + "heck 0.4.0", + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "clap_derive" version = "4.0.21" @@ -903,6 +934,15 @@ dependencies = [ "num-traits 0.1.43", ] +[[package]] +name = "env_logger" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" +dependencies = [ + "log", +] + [[package]] name = "errno" version = "0.2.8" @@ -1362,6 +1402,30 @@ dependencies = [ "hashbrown 0.12.3", ] +[[package]] +name = "inferno" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7207d75fcf6c1868f1390fc1c610431fe66328e9ee6813330a041ef6879eca1" +dependencies = [ + "ahash 0.8.2", + "atty", + "clap 3.2.23", + "crossbeam-channel", + "crossbeam-utils", + "dashmap", + "env_logger", + "indexmap", + "itoa 1.0.4", + "log", + "num-format", + "num_cpus", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + [[package]] name = "inout" version = "0.1.3" @@ -1802,6 +1866,16 @@ dependencies = [ "serde", ] +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa 1.0.4", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -2251,6 +2325,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quote" version = "1.0.21" @@ -2481,6 +2564,15 @@ dependencies = [ "winreg", ] +[[package]] +name = "rgb" +version = "0.8.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.16.20" @@ -3054,6 +3146,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + [[package]] name = "strsim" version = "0.10.0" @@ -3151,6 +3249,7 @@ dependencies = [ "cassandra-cpp", "cassandra-protocol", "cdrs-tokio", + "inferno", "openssl", "ordered-float 3.4.0", "rcgen", diff --git a/shotover-proxy/example-configs/cassandra-cluster-v4/topology-encode.yaml b/shotover-proxy/example-configs/cassandra-cluster-v4/topology-encode.yaml new file mode 100644 index 000000000..e241c1c45 --- /dev/null +++ b/shotover-proxy/example-configs/cassandra-cluster-v4/topology-encode.yaml @@ -0,0 +1,22 @@ +--- +sources: + cassandra_prod: + Cassandra: + listen_addr: "127.0.0.1:9042" +chain_config: + main_chain: + - DebugForceEncode: + encode_requests: true + encode_responses: true + - CassandraSinkCluster: + first_contact_points: ["172.16.1.2:9044", "172.16.1.3:9044"] + local_shotover_host_id: "2dd022d6-2937-4754-89d6-02d2933a8f7a" + shotover_nodes: + - address: "127.0.0.1:9042" + data_center: "dc1" + rack: "rack1" + host_id: "2dd022d6-2937-4754-89d6-02d2933a8f7a" + connect_timeout_ms: 3000 + +source_to_chain_mapping: + cassandra_prod: main_chain diff --git a/shotover-proxy/example-configs/cassandra-cluster-v4/topology.yaml b/shotover-proxy/example-configs/cassandra-cluster-v4/topology.yaml index e241c1c45..08bbc65e7 100644 --- a/shotover-proxy/example-configs/cassandra-cluster-v4/topology.yaml +++ b/shotover-proxy/example-configs/cassandra-cluster-v4/topology.yaml @@ -5,9 +5,6 @@ sources: listen_addr: "127.0.0.1:9042" chain_config: main_chain: - - DebugForceEncode: - encode_requests: true - encode_responses: true - CassandraSinkCluster: first_contact_points: ["172.16.1.2:9044", "172.16.1.3:9044"] local_shotover_host_id: "2dd022d6-2937-4754-89d6-02d2933a8f7a" diff --git a/shotover-proxy/examples/cassandra_cluster_flamegraph.rs b/shotover-proxy/examples/cassandra_cluster_flamegraph.rs index 915e6ecf1..622c46092 100644 --- a/shotover-proxy/examples/cassandra_cluster_flamegraph.rs +++ b/shotover-proxy/examples/cassandra_cluster_flamegraph.rs @@ -1,7 +1,14 @@ use test_helpers::docker_compose::DockerCompose; +use test_helpers::flamegraph::Perf; use test_helpers::latte::Latte; use test_helpers::shotover_process::shotover_from_topology_file; +// To get useful results you will need to modify the Cargo.toml like: +// [profile.release] +// #lto = "fat" +// codegen-units = 1 +// debug = true + #[tokio::main] async fn main() { test_helpers::bench::init(); @@ -15,9 +22,12 @@ async fn main() { let shotover = shotover_from_topology_file(&format!("{}/topology.yaml", config_dir)).await; + let perf = Perf::new(shotover.child.as_ref().unwrap().id().unwrap()); + println!("Benching Shotover ..."); latte.bench(bench, "localhost:9042"); shotover.shutdown_and_then_consume_events(&[]).await; + perf.flamegraph(); } } diff --git a/shotover-proxy/tests/cassandra_int_tests/mod.rs b/shotover-proxy/tests/cassandra_int_tests/mod.rs index d73ba0fb3..215e15fc5 100644 --- a/shotover-proxy/tests/cassandra_int_tests/mod.rs +++ b/shotover-proxy/tests/cassandra_int_tests/mod.rs @@ -179,7 +179,7 @@ async fn cluster_single_rack_v4(#[case] driver: CassandraDriver) { }; { let _shotover_manager = ShotoverManager::from_topology_file( - "example-configs/cassandra-cluster-v4/topology.yaml", + "example-configs/cassandra-cluster-v4/topology-encode.yaml", ); standard_test_suite(&connection, driver).await; diff --git a/test-helpers/Cargo.toml b/test-helpers/Cargo.toml index 2898bf217..5877452bf 100644 --- a/test-helpers/Cargo.toml +++ b/test-helpers/Cargo.toml @@ -30,3 +30,4 @@ uuid = { version = "1.0.0", features = ["serde", "v4"] } redis = { version = "0.22.0", features = ["tokio-comp", "cluster"] } tokio-io-timeout = "1.1.1" tokio-openssl = "0.6.2" +inferno = "0.11.13" diff --git a/test-helpers/src/flamegraph.rs b/test-helpers/src/flamegraph.rs new file mode 100644 index 000000000..6c48ce8db --- /dev/null +++ b/test-helpers/src/flamegraph.rs @@ -0,0 +1,79 @@ +use inferno::collapse::perf::{Folder, Options as CollapseOptions}; +use inferno::collapse::Collapse; +use inferno::flamegraph::{from_reader, Options}; +use std::fs::File; +use std::io::{BufReader, BufWriter}; +use std::process::{Child, Command}; + +use crate::docker_compose::run_command; + +pub struct Perf(Child); + +impl Perf { + /// Begin recording perf data to perf.data + pub fn new(pid: u32) -> Perf { + // remove perf.data if it exists to avoid generating perf.data.old clutter + std::fs::remove_file("perf.data").ok(); + + // Run `sudo ls` so that we can get sudo to stop asking for password for a while. + // It also lets us block until the user has entered the password, otherwise the rest of the test would continue before `perf` has started. + // TODO: It would be more robust to get a single root prompt and then keep feeding commands into it. + // But that would require a bunch of work so its out of scope for now. + run_command("sudo", &["ls"]).unwrap(); + + let mut command = Command::new("sudo"); + command.args([ + "perf", + "record", + "-F", + "997", + "--call-graph", + "dwarf,16384", + "-g", + "-o", + "perf.data", + "-p", + &pid.to_string(), + ]); + + Perf(command.spawn().unwrap()) + } + + /// Blocks until the recorded process has terminated + /// Generates a flamegraph at flamegraph.svg + pub fn flamegraph(mut self) { + self.0.wait().unwrap(); + + run_command( + "sudo", + &["chown", &std::env::var("USER").unwrap(), "perf.data"], + ) + .unwrap(); + + let output = Command::new("perf").args(["script"]).output().unwrap(); + if !output.status.success() { + panic!( + "unable to run 'perf script': ({}) {}", + output.status, + std::str::from_utf8(&output.stderr).unwrap() + ); + } + let output = output.stdout; + + let perf_reader = BufReader::new(&*output); + let mut collapsed = vec![]; + let collapsed_writer = BufWriter::new(&mut collapsed); + + Folder::from(CollapseOptions::default()) + .collapse(perf_reader, collapsed_writer) + .unwrap(); + + let collapsed_reader = BufReader::new(&*collapsed); + + let filename = "flamegraph.svg"; + println!("writing flamegraph to {filename:?}"); + let flamegraph_writer = BufWriter::new(File::create(filename).unwrap()); + + from_reader(&mut Options::default(), collapsed_reader, flamegraph_writer).unwrap(); + } +} diff --git a/test-helpers/src/lib.rs b/test-helpers/src/lib.rs index 4eeeebd1e..7f7b51d52 100644 --- a/test-helpers/src/lib.rs +++ b/test-helpers/src/lib.rs @@ -4,6 +4,7 @@ pub mod bench; pub mod cert; pub mod connection; pub mod docker_compose; +pub mod flamegraph; pub mod latte; pub mod lazy; pub mod shotover_process;