diff --git a/.gitignore b/.gitignore index 8a219191a1..13c500cfca 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ # Backup files generated by rustfmt **/*.rs.bk + +*.flamegraph.svg diff --git a/Cargo.lock b/Cargo.lock index a4f6b78e21..da14d11d5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,6 +23,19 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -102,6 +115,12 @@ version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +[[package]] +name = "arrayvec" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" + [[package]] name = "async-compression" version = "0.4.10" @@ -206,6 +225,12 @@ version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +[[package]] +name = "bytemuck" +version = "1.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b236fc92302c97ed75b38da1f4917b5cdda4984745740f153a5d3059e48d725e" + [[package]] name = "bytes" version = "1.6.0" @@ -223,7 +248,7 @@ dependencies = [ "futures", "hex", "libc", - "memmap2", + "memmap2 0.5.10", "miette", "reflink-copy", "serde", @@ -409,6 +434,15 @@ version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" +[[package]] +name = "cpp_demangle" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e8227005286ec39567949b33df9896bcadfa6051bccca2488129f108ca23119" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.12" @@ -462,6 +496,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "debugid" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" +dependencies = [ + "uuid", +] + [[package]] name = "deranged" version = "0.3.11" @@ -616,6 +659,18 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" +[[package]] +name = "findshlibs" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" +dependencies = [ + "cc", + "lazy_static", + "libc", + "winapi", +] + [[package]] name = "flate2" version = "1.0.30" @@ -771,7 +826,9 @@ dependencies = [ "indicatif", "lazy_static", "log", + "pprof", "pretty_assertions", + "rand", "regex", "secrecy", "shellexpand", @@ -1252,6 +1309,24 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "inferno" +version = "0.11.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c77a3ae7d4761b9c64d2c030f70746ceb8cfba32dce0325a56792e0a4816c31" +dependencies = [ + "ahash", + "indexmap", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml", + "rgb", + "str_stack", +] + [[package]] name = "instant" version = "0.1.12" @@ -1421,6 +1496,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + [[package]] name = "log" version = "0.4.21" @@ -1442,6 +1527,15 @@ dependencies = [ "libc", ] +[[package]] +name = "memmap2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" +dependencies = [ + "libc", +] + [[package]] name = "miette" version = "5.10.0" @@ -1508,6 +1602,17 @@ dependencies = [ "semver", ] +[[package]] +name = "nix" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +dependencies = [ + "bitflags 1.3.2", + "cfg-if", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -1524,6 +1629,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1570,6 +1685,29 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets 0.52.5", +] + [[package]] name = "parse-zoneinfo" version = "0.3.1" @@ -1724,6 +1862,27 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "pprof" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef5c97c51bd34c7e742402e216abdeb44d415fbe6ae41d56b114723e953711cb" +dependencies = [ + "backtrace", + "cfg-if", + "findshlibs", + "inferno", + "libc", + "log", + "nix", + "once_cell", + "parking_lot", + "smallvec", + "symbolic-demangle", + "tempfile", + "thiserror", +] + [[package]] name = "ppv-lite86" version = "0.2.17" @@ -1755,6 +1914,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.2" @@ -1841,6 +2009,15 @@ dependencies = [ "getrandom", ] +[[package]] +name = "redox_syscall" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" +dependencies = [ + "bitflags 2.5.0", +] + [[package]] name = "redox_users" version = "0.4.5" @@ -1992,6 +2169,15 @@ dependencies = [ "tower-service", ] +[[package]] +name = "rgb" +version = "0.8.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ade4539f42266ded9e755c605bdddf546242b2c961b03b06a7375260788a0523" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.8" @@ -2180,6 +2366,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + [[package]] name = "sct" version = "0.7.1" @@ -2374,6 +2566,18 @@ dependencies = [ "xxhash-rust", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[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.11.1" @@ -2386,6 +2590,29 @@ version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +[[package]] +name = "symbolic-common" +version = "12.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16629323a4ec5268ad23a575110a724ad4544aae623451de600c747bf87b36cf" +dependencies = [ + "debugid", + "memmap2 0.9.4", + "stable_deref_trait", + "uuid", +] + +[[package]] +name = "symbolic-demangle" +version = "12.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c043a45f08f41187414592b3ceb53fb0687da57209cc77401767fb69d5b596" +dependencies = [ + "cpp_demangle", + "rustc-demangle", + "symbolic-common", +] + [[package]] name = "syn" version = "1.0.109" @@ -2894,6 +3121,12 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "uuid" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" + [[package]] name = "vcpkg" version = "0.2.15" @@ -3022,6 +3255,22 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + [[package]] name = "winapi-util" version = "0.1.8" @@ -3031,6 +3280,12 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows" version = "0.56.0" @@ -3282,6 +3537,26 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +[[package]] +name = "zerocopy" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.7.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.63", +] + [[package]] name = "zeroize" version = "1.7.0" diff --git a/Cargo.toml b/Cargo.toml index 9f47692a1d..bd39a2d960 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -29,4 +29,5 @@ strip = true [profile.bench] opt-level = 3 -debug = false +debug = true # used by the profiler +strip = false # keep symbols for the profiler diff --git a/git-cliff/Cargo.toml b/git-cliff/Cargo.toml index e3ff7c929b..2c7cf25332 100644 --- a/git-cliff/Cargo.toml +++ b/git-cliff/Cargo.toml @@ -36,6 +36,9 @@ gitlab = ["remote", "git-cliff-core/gitlab"] gitea = ["remote", "git-cliff-core/gitea"] # enable Bitbucket integration bitbucket = ["remote", "git-cliff-core/bitbucket"] +# performance profiler +profiler = ["dep:pprof", "dep:rand", "profiler-flamegraph"] +profiler-flamegraph = ["pprof/flamegraph"] [dependencies] glob.workspace = true @@ -51,6 +54,8 @@ shellexpand = "3.1.0" update-informer = { version = "1.1.0", optional = true } indicatif = { version = "0.17.8", optional = true } env_logger = "0.10.2" +pprof = { version = "0.13", optional = true } +rand = { version = "0.8.4", optional = true } [dependencies.git-cliff-core] version = "2.4.0" # managed by release.sh diff --git a/git-cliff/src/main.rs b/git-cliff/src/main.rs index 2dd58ba3ab..5c1e901e4c 100644 --- a/git-cliff/src/main.rs +++ b/git-cliff/src/main.rs @@ -5,7 +5,27 @@ use git_cliff_core::error::Result; use std::env; use std::process; +#[cfg(feature = "profiler")] +mod profiler; +#[cfg(feature = "profiler")] +use profiler::{ + finish_profiling, + start_profiling, +}; + fn main() -> Result<()> { + // Initialize the profiler guard if the feature is enabled + let mut _profiler_guard = None; + #[cfg(feature = "profiler")] + { + _profiler_guard = Some(start_profiling()); + } + #[cfg(not(feature = "profiler"))] + { + _profiler_guard = Some(()); + } + + // Parse the command line arguments let args = Opt::parse(); if args.verbose == 1 { env::set_var("RUST_LOG", "debug"); @@ -15,11 +35,21 @@ fn main() -> Result<()> { env::set_var("RUST_LOG", "info"); } logger::init()?; - match git_cliff::run(args) { - Ok(_) => process::exit(0), + + // Run git-cliff + let exit_code = match git_cliff::run(args) { + Ok(_) => 0, Err(e) => { log::error!("{}", e); - process::exit(1) + 1 } + }; + + // Report the profiler if the feature is enabled + #[cfg(feature = "profiler")] + { + finish_profiling(_profiler_guard.unwrap()); } + + process::exit(exit_code); } diff --git a/git-cliff/src/profiler.rs b/git-cliff/src/profiler.rs new file mode 100644 index 0000000000..b02e642b48 --- /dev/null +++ b/git-cliff/src/profiler.rs @@ -0,0 +1,39 @@ +pub(crate) fn start_profiling() -> pprof::ProfilerGuard<'static> { + return pprof::ProfilerGuardBuilder::default() + .frequency(1000) + .blocklist(&["libc", "libgcc", "pthread", "vdso"]) + .build() + .unwrap(); +} + +pub(crate) fn finish_profiling(profiler_guard: pprof::ProfilerGuard) { + match profiler_guard.report().build() { + Ok(report) => { + #[cfg(feature = "profiler-flamegraph")] + { + use std::fs::File; + let random = rand::random::(); + + match File::create(format!( + "{}.{}.flamegraph.svg", + "git-cliff", random + )) { + Ok(file) => { + report.flamegraph(file).unwrap(); + } + Err(err) => { + log::error!("Failed to create flamegraph file {}", err); + } + } + } + + #[cfg(not(feature = "profiler-flamegraph"))] + { + log::info!("profiling report: {:?}", &report); + } + } + Err(err) => { + log::error!("Failed to build profiler report {}", err); + } + } +}