torrust_tracker/console/profiling.rs
1//! This binary is used for profiling with [valgrind](https://valgrind.org/)
2//! and [kcachegrind](https://kcachegrind.github.io/).
3//!
4//! # Requirements
5//!
6//! [valgrind](https://valgrind.org/) and [kcachegrind](https://kcachegrind.github.io/).
7//!
8//! On Ubuntu you can install them with:
9//!
10//! ```text
11//! sudo apt install valgrind kcachegrind
12//! ```
13//!
14//! > NOTICE: valgrind executes the program you wan to profile and waits until
15//! > it ends. Since the tracker is a service and does not end the profiling
16//! > binary accepts an arguments with the duration you want to run the tracker,
17//! > so that it terminates automatically after that period of time.
18//!
19//! # Run profiling
20//!
21//! To run the profiling you have to:
22//!
23//! 1. Build and run the tracker for profiling.
24//! 2. Run the aquatic UDP load test tool to start collecting data in the tracker.
25//!
26//! Build and run the tracker for profiling:
27//!
28//! ```text
29//! RUSTFLAGS='-g' cargo build --release --bin profiling \
30//! && export TORRUST_TRACKER_CONFIG_TOML_PATH="./share/default/config/tracker.udp.benchmarking.toml" \
31//! && valgrind \
32//! --tool=callgrind \
33//! --callgrind-out-file=callgrind.out \
34//! --collect-jumps=yes \
35//! --simulate-cache=yes \
36//! ./target/release/profiling 60
37//! ```
38//!
39//! The output should be something like:
40//!
41//! ```text
42//! RUSTFLAGS='-g' cargo build --release --bin profiling \
43//! && export TORRUST_TRACKER_CONFIG_TOML_PATH="./share/default/config/tracker.udp.benchmarking.toml" \
44//! && valgrind \
45//! --tool=callgrind \
46//! --callgrind-out-file=callgrind.out \
47//! --collect-jumps=yes \
48//! --simulate-cache=yes \
49//! ./target/release/profiling 60
50//!
51//! Compiling torrust-tracker v3.0.0-alpha.12-develop (/home/developer/Documents/git/committer/me/github/torrust/torrust-tracker)
52//! Finished `release` profile [optimized + debuginfo] target(s) in 1m 15s
53//! ==122801== Callgrind, a call-graph generating cache profiler
54//! ==122801== Copyright (C) 2002-2017, and GNU GPL'd, by Josef Weidendorfer et al.
55//! ==122801== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info
56//! ==122801== Command: ./target/release/profiling 60
57//! ==122801==
58//! --122801-- warning: L3 cache found, using its data for the LL simulation.
59//! ==122801== For interactive control, run 'callgrind_control -h'.
60//! Loading configuration file: `./share/default/config/tracker.udp.benchmarking.toml` ...
61//! Torrust successfully shutdown.
62//! ==122801==
63//! ==122801== Events : Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw
64//! ==122801== Collected : 1160654816 278135882 247755311 24453652 12650490 16315690 10932 2481624 4832145
65//! ==122801==
66//! ==122801== I refs: 1,160,654,816
67//! ==122801== I1 misses: 24,453,652
68//! ==122801== LLi misses: 10,932
69//! ==122801== I1 miss rate: 2.11%
70//! ==122801== LLi miss rate: 0.00%
71//! ==122801==
72//! ==122801== D refs: 525,891,193 (278,135,882 rd + 247,755,311 wr)
73//! ==122801== D1 misses: 28,966,180 ( 12,650,490 rd + 16,315,690 wr)
74//! ==122801== LLd misses: 7,313,769 ( 2,481,624 rd + 4,832,145 wr)
75//! ==122801== D1 miss rate: 5.5% ( 4.5% + 6.6% )
76//! ==122801== LLd miss rate: 1.4% ( 0.9% + 2.0% )
77//! ==122801==
78//! ==122801== LL refs: 53,419,832 ( 37,104,142 rd + 16,315,690 wr)
79//! ==122801== LL misses: 7,324,701 ( 2,492,556 rd + 4,832,145 wr)
80//! ==122801== LL miss rate: 0.4% ( 0.2% + 2.0% )
81//! ```
82//!
83//! > NOTICE: We are using an specific tracker configuration for profiling that
84//! > removes all features except the UDP tracker and sets the logging level to `error`.
85//!
86//! Build the aquatic UDP load test command:
87//!
88//! ```text
89//! cd /tmp
90//! git clone git@github.com:greatest-ape/aquatic.git
91//! cd aquatic
92//! cargo build --profile=release-debug -p aquatic_udp_load_test
93//! ./target/release-debug/aquatic_udp_load_test -p > "load-test-config.toml"
94//! ```
95//!
96//! Modify the "load-test-config.toml" file to change the UDP tracker port from
97//! `3000` to `6969`.
98//!
99//! Running the aquatic UDP load test command:
100//!
101//! ```text
102//! ./target/release-debug/aquatic_udp_load_test -c "load-test-config.toml"
103//! ```
104//!
105//! The output should be something like this:
106//!
107//! ```text
108//! Starting client with config: Config {
109//! server_address: 127.0.0.1:6969,
110//! log_level: Error,
111//! workers: 1,
112//! duration: 0,
113//! summarize_last: 0,
114//! extra_statistics: true,
115//! network: NetworkConfig {
116//! multiple_client_ipv4s: true,
117//! sockets_per_worker: 4,
118//! recv_buffer: 8000000,
119//! },
120//! requests: RequestConfig {
121//! number_of_torrents: 1000000,
122//! number_of_peers: 2000000,
123//! scrape_max_torrents: 10,
124//! announce_peers_wanted: 30,
125//! weight_connect: 50,
126//! weight_announce: 50,
127//! weight_scrape: 1,
128//! peer_seeder_probability: 0.75,
129//! },
130//! }
131//!
132//! Requests out: 45097.51/second
133//! Responses in: 4212.70/second
134//! - Connect responses: 2098.15
135//! - Announce responses: 2074.95
136//! - Scrape responses: 39.59
137//! - Error responses: 0.00
138//! Peers per announce response: 0.00
139//! Announce responses per info hash:
140//! - p10: 1
141//! - p25: 1
142//! - p50: 1
143//! - p75: 2
144//! - p90: 3
145//! - p95: 4
146//! - p99: 6
147//! - p99.9: 8
148//! - p100: 10
149//! ```
150//!
151//! After running the tracker for some seconds the tracker will automatically stop
152//! and `valgrind`will write the file `callgrind.out` with the data.
153//!
154//! You can now analyze the collected data with:
155//!
156//! ```text
157//! kcachegrind callgrind.out
158//! ```
159use std::env;
160use std::time::Duration;
161
162use tokio::time::sleep;
163
164use crate::{app, bootstrap};
165
166pub async fn run() {
167 // Parse command line arguments
168 let args: Vec<String> = env::args().collect();
169
170 // Ensure an argument for duration is provided
171 if args.len() != 2 {
172 eprintln!("Usage: {} <duration_in_seconds>", args[0]);
173 return;
174 }
175
176 // Parse duration argument
177 let Ok(duration_secs) = args[1].parse::<u64>() else {
178 eprintln!("Invalid duration provided");
179 return;
180 };
181
182 let (config, tracker) = bootstrap::app::setup();
183
184 let jobs = app::start(&config, tracker).await;
185
186 // Run the tracker for a fixed duration
187 let run_duration = sleep(Duration::from_secs(duration_secs));
188
189 tokio::select! {
190 () = run_duration => {
191 tracing::info!("Torrust timed shutdown..");
192 },
193 _ = tokio::signal::ctrl_c() => {
194 tracing::info!("Torrust shutting down via Ctrl+C ...");
195 // Await for all jobs to shutdown
196 futures::future::join_all(jobs).await;
197 }
198 }
199
200 println!("Torrust successfully shutdown.");
201}