Skip to content

Commit

Permalink
Add flamegraph generation to flamegraph example (#968)
Browse files Browse the repository at this point in the history
  • Loading branch information
rukai authored Jan 9, 2023
1 parent c2dafcb commit 201047b
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 5 deletions.
101 changes: 100 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
10 changes: 10 additions & 0 deletions shotover-proxy/examples/cassandra_cluster_flamegraph.rs
Original file line number Diff line number Diff line change
@@ -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();
Expand All @@ -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();
}
}
2 changes: 1 addition & 1 deletion shotover-proxy/tests/cassandra_int_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions test-helpers/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
79 changes: 79 additions & 0 deletions test-helpers/src/flamegraph.rs
Original file line number Diff line number Diff line change
@@ -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();
}
}
1 change: 1 addition & 0 deletions test-helpers/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 201047b

Please sign in to comment.