diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 89aae4a0..3ad60123 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -25,11 +25,17 @@ jobs: command: fmt args: --all -- --check - - name: Run cargo clippy + - name: Run cargo clippy prost uses: actions-rs/cargo@v1.0.3 with: command: clippy - args: --all-features -- -D warnings + args: --features flamegraph,prost-codec -- -D warnings + + - name: Run cargo clippy protobuf + uses: actions-rs/cargo@v1.0.3 + with: + command: clippy + args: --features flamegraph,protobuf-codec -- -D warnings build: name: Build @@ -52,11 +58,17 @@ jobs: target: ${{ matrix.target }} override: true - - name: Run cargo build + - name: Run cargo build prost + uses: actions-rs/cargo@v1.0.3 + with: + command: build + args: --features flamegraph,prost-codec --target ${{ matrix.target }} + + - name: Run cargo build protobuf uses: actions-rs/cargo@v1.0.3 with: command: build - args: --all-features --target ${{ matrix.target }} + args: --features flamegraph,protobuf-codec --target ${{ matrix.target }} test: name: Test @@ -77,8 +89,14 @@ jobs: toolchain: ${{ matrix.toolchain }} override: true - - name: Run cargo test + - name: Run cargo test prost + uses: actions-rs/cargo@v1.0.3 + with: + command: test + args: --features flamegraph,prost-codec + + - name: Run cargo test protobuf uses: actions-rs/cargo@v1.0.3 with: command: test - args: --all-features + args: --features flamegraph,protobuf-codec diff --git a/.gitignore b/.gitignore index b5ef7c93..524dafb7 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,5 @@ Cargo.lock /target #**/*.rs.bk #Cargo.lock + +.idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 19f9a8e5..669d8af3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- Add rust-protobuf support by adding protobuf-codec features (#106) + +### Changed +- protobuf feature is renamed to prost-codec to align all other tikv projects (#106) + ## [0.6.2] - 2021-12-24 ### Added - implement `Clone` for `ProfilerGuardBuilder` [@yangkeao](https://github.com/YangKeao) @@ -151,4 +157,4 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [0.1.0] - 2019-10-22 ### Added - Support profiling with signal handler [@yangkeao](https://github.com/YangKeao) -- Support generating flamegraph [@yangkeao](https://github.com/YangKeao) \ No newline at end of file +- Support generating flamegraph [@yangkeao](https://github.com/YangKeao) diff --git a/Cargo.toml b/Cargo.toml index 2d6b981e..2b622ffb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,7 +12,10 @@ readme = "README.md" [features] default = ["cpp"] flamegraph = ["inferno"] -protobuf = ["prost", "prost-derive", "prost-build"] +# A private feature to indicate either prost-codec or protobuf-codec is enabled. +_protobuf = [] +prost-codec = ["prost", "prost-derive", "prost-build", "_protobuf"] +protobuf-codec = ["protobuf", "protobuf-codegen-pure", "_protobuf"] cpp = ["symbolic-demangle/cpp"] [dependencies] @@ -31,6 +34,7 @@ smallvec = "1.7" inferno = { version = "0.10", default-features = false, features = ["nameattr"], optional = true } prost = { version = "0.9", optional = true } prost-derive = { version = "0.9", optional = true } +protobuf = { version = "2.0", optional = true } criterion = {version = "0.3", optional = true} [dependencies.symbolic-demangle] @@ -44,6 +48,7 @@ rand = "0.8.0" [build-dependencies] prost-build = { version = "0.9", optional = true } +protobuf-codegen-pure = { version = "2.0", optional = true } [[example]] name = "flamegraph" diff --git a/build.rs b/build.rs index bfe04ca3..6db69bb8 100644 --- a/build.rs +++ b/build.rs @@ -1,6 +1,30 @@ // Copyright 2019 TiKV Project Authors. Licensed under Apache-2.0. +#[cfg(feature = "protobuf-codec")] +// Allow deprecated as TiKV pin versions to a outdated one. +#[allow(deprecated)] +fn generate_protobuf() { + use std::io::Write; + + let out_dir = std::env::var("OUT_DIR").unwrap(); + protobuf_codegen_pure::run(protobuf_codegen_pure::Args { + out_dir: &out_dir, + includes: &["proto"], + input: &["proto/profile.proto"], + customize: protobuf_codegen_pure::Customize { + generate_accessors: Some(false), + lite_runtime: Some(true), + ..Default::default() + }, + }) + .unwrap(); + let mut f = std::fs::File::create(format!("{}/mod.rs", out_dir)).unwrap(); + write!(f, "pub mod profile;").unwrap(); +} + fn main() { - #[cfg(feature = "protobuf")] + #[cfg(feature = "prost-codec")] prost_build::compile_protos(&["proto/profile.proto"], &["proto/"]).unwrap(); + #[cfg(feature = "protobuf-codec")] + generate_protobuf(); } diff --git a/examples/profile_proto.rs b/examples/profile_proto.rs index f6a5ad57..7ffb7884 100644 --- a/examples/profile_proto.rs +++ b/examples/profile_proto.rs @@ -108,7 +108,10 @@ fn main() { let profile = report.pprof().unwrap(); let mut content = Vec::new(); + #[cfg(not(feature = "protobuf-codec"))] profile.encode(&mut content).unwrap(); + #[cfg(feature = "protobuf-codec")] + profile.write_to_vec(&mut content).unwrap(); file.write_all(&content).unwrap(); println!("report: {:?}", report); diff --git a/proto/profile.proto b/proto/profile.proto index 6f141c78..4ea85164 100644 --- a/proto/profile.proto +++ b/proto/profile.proto @@ -93,7 +93,8 @@ message Profile { // ValueType describes the semantics and measurement units of a value. message ValueType { - int64 type = 1; // Index into string table. + // Rename it from type to ty to avoid using keyword in Rust. + int64 ty = 1; // Index into string table. int64 unit = 2; // Index into string table. } diff --git a/src/criterion.rs b/src/criterion.rs index 87d5bd94..a8e00a9d 100644 --- a/src/criterion.rs +++ b/src/criterion.rs @@ -2,14 +2,14 @@ #[cfg(feature = "flamegraph")] use crate::flamegraph::Options as FlamegraphOptions; -#[cfg(feature = "protobuf")] +#[cfg(feature = "_protobuf")] use crate::protos::Message; use crate::ProfilerGuard; use criterion::profiler::Profiler; use std::fs::File; -#[cfg(feature = "protobuf")] +#[cfg(feature = "_protobuf")] use std::io::Write; use std::marker::PhantomData; use std::os::raw::c_int; @@ -20,7 +20,7 @@ pub enum Output<'a> { #[cfg(feature = "flamegraph")] Flamegraph(Option>), - #[cfg(feature = "protobuf")] + #[cfg(feature = "_protobuf")] Protobuf, #[deprecated( @@ -45,7 +45,7 @@ impl<'a, 'b> PProfProfiler<'a, 'b> { } } -#[cfg(not(any(feature = "protobuf", feature = "flamegraph")))] +#[cfg(not(any(feature = "_protobuf", feature = "flamegraph")))] compile_error!("Either feature \"protobuf\" or \"flamegraph\" must be enabled when \"criterion\" feature is enabled."); impl<'a, 'b> Profiler for PProfProfiler<'a, 'b> { @@ -59,7 +59,7 @@ impl<'a, 'b> Profiler for PProfProfiler<'a, 'b> { let filename = match self.output { #[cfg(feature = "flamegraph")] Output::Flamegraph(_) => "flamegraph.svg", - #[cfg(feature = "protobuf")] + #[cfg(feature = "_protobuf")] Output::Protobuf => "profile.pb", // This is `""` but not `unreachable!()`, because `unreachable!()` // will result in another compile error, so that the user may not @@ -86,16 +86,21 @@ impl<'a, 'b> Profiler for PProfProfiler<'a, 'b> { .expect("Error while writing flamegraph"); } - #[cfg(feature = "protobuf")] + #[cfg(feature = "_protobuf")] Output::Protobuf => { let mut output_file = output_file; let profile = profiler.report().build().unwrap().pprof().unwrap(); let mut content = Vec::new(); + #[cfg(not(feature = "protobuf-codec"))] profile .encode(&mut content) .expect("Error while encoding protobuf"); + #[cfg(feature = "protobuf-codec")] + profile + .write_to_vec(&mut content) + .expect("Error while encoding protobuf"); output_file .write_all(&content) diff --git a/src/lib.rs b/src/lib.rs index 4336939d..4fa36c44 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,12 +60,21 @@ pub use self::report::{Report, ReportBuilder}; #[cfg(feature = "flamegraph")] pub use inferno::flamegraph; -#[cfg(feature = "protobuf")] +#[cfg(all(feature = "prost-codec", not(feature = "protobuf-codec")))] pub mod protos { pub use prost::Message; include!(concat!(env!("OUT_DIR"), "/perftools.profiles.rs")); } +#[cfg(feature = "protobuf-codec")] +pub mod protos { + pub use protobuf::Message; + + include!(concat!(env!("OUT_DIR"), "/mod.rs")); + + pub use self::profile::*; +} + #[cfg(feature = "criterion")] pub mod criterion; diff --git a/src/report.rs b/src/report.rs index 3daaf5f4..a19b940e 100644 --- a/src/report.rs +++ b/src/report.rs @@ -209,7 +209,9 @@ mod flamegraph { } } -#[cfg(feature = "protobuf")] +#[cfg(feature = "_protobuf")] +#[allow(clippy::useless_conversion)] +#[allow(clippy::needless_update)] mod protobuf { use super::*; use crate::protos; @@ -278,10 +280,11 @@ mod protobuf { let line = protos::Line { function_id, line: lineno as i64, + ..protos::Line::default() }; let loc = protos::Location { id: function_id, - line: vec![line], + line: vec![line].into(), ..protos::Location::default() }; // the fn_tbl has the same length with loc_tbl @@ -302,24 +305,27 @@ mod protobuf { *count as i64, *count as i64 * 1_000_000_000 / self.timing.frequency as i64, ], - label: vec![thread_name], + label: vec![thread_name].into(), + ..Default::default() }; samples.push(sample); } let samples_value = protos::ValueType { - r#type: *strings.get(SAMPLES).unwrap() as i64, + ty: *strings.get(SAMPLES).unwrap() as i64, unit: *strings.get(COUNT).unwrap() as i64, + ..Default::default() }; let time_value = protos::ValueType { - r#type: *strings.get(CPU).unwrap() as i64, + ty: *strings.get(CPU).unwrap() as i64, unit: *strings.get(NANOSECONDS).unwrap() as i64, + ..Default::default() }; let profile = protos::Profile { - sample_type: vec![samples_value, time_value.clone()], - sample: samples, - string_table: str_tbl, - function: fn_tbl, - location: loc_tbl, + sample_type: vec![samples_value, time_value.clone()].into(), + sample: samples.into(), + string_table: str_tbl.into(), + function: fn_tbl.into(), + location: loc_tbl.into(), time_nanos: self .timing .start_time @@ -327,7 +333,7 @@ mod protobuf { .unwrap_or_default() .as_nanos() as i64, duration_nanos: self.timing.duration.as_nanos() as i64, - period_type: Some(time_value), + period_type: Some(time_value).into(), period: 1_000_000_000 / self.timing.frequency as i64, ..protos::Profile::default() };